Skip to content

Commit b2ee37f

Browse files
committed
修改 native 中样式 bug,文档修改
1 parent bbd12c6 commit b2ee37f

5 files changed

Lines changed: 67 additions & 73 deletions

File tree

packages/docs/demos/native/EditModeDemo.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ export default function EditModeDemo() {
9292
切换到「行编辑」模式,修改城市字段后点「取消」,观察城市是否还原
9393
</p>
9494
<EditableTable<User>
95+
bordered
9596
rowKey="id"
9697
dataSource={dataSource}
9798
onChange={setDataSource}

packages/docs/native/api.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
| `editableMode` | `'all' \| 'row'` | `'all'` | 编辑模式 |
1212
| `onSubmit` | `(data: T[]) => void` || 提交回调,校验通过后触发 |
1313
| `validateTrigger` | `'submit' \| 'change'` | `'submit'` | 校验触发时机 |
14-
| `scrollY` | `number` | `500` | 虚拟滚动容器高度(px) |
14+
| `scrollY` | `number` | | 虚拟滚动容器高度(px),传入时启用虚拟滚动 |
1515
| `emptyText` | `string` | `'暂无数据'` | 空数据提示文案 |
1616
| `className` | `string` || 容器类名 |
1717
| `style` | `CSSProperties` || 容器样式 |
@@ -27,7 +27,7 @@
2727
| `editable` | `boolean` | `true` | 是否可编辑 |
2828
| `editRender` | `(props: EditRenderProps<T>) => ReactNode` || 自定义编辑态渲染 |
2929
| `rules` | `Rule[]` || 校验规则 |
30-
| `onFieldChange` | `(value: any, row: T) => Partial<T> \| undefined` || 字段联动回调 |
30+
| `onFieldChange` | `(value: any, row: T) => Partial<T> \| undefined \| Promise<Partial<T> \| undefined>` || 字段联动回调,支持异步 |
3131
| `render` | `(value: any, row: T, rowIndex: number) => ReactNode` || 自定义只读态渲染 |
3232

3333
## EditRenderProps
@@ -58,7 +58,10 @@
5858
| 方法 | 参数 | 说明 |
5959
|------|------|------|
6060
| `addRow` | `defaults?: Partial<T>` | 在末尾新增一行 |
61+
| `insertRow` | `rowIndex: number, defaults?: Partial<T>` | 在指定位置插入一行 |
6162
| `removeRow` | `rowIndex: number` | 删除指定行 |
63+
| `updateRow` | `rowIndex: number, updates: Partial<T>` | 部分更新指定行数据 |
6264
| `moveUp` | `rowIndex: number` | 上移指定行 |
6365
| `moveDown` | `rowIndex: number` | 下移指定行 |
6466
| `getData` || 获取当前数据 |
67+
| `validateAll` || 全量校验,返回 `{ isValid: boolean; errors: Record<string, string> }` |

packages/docs/native/row-ops.md

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ tableRef.current?.removeRow(2); // 删除第三行
6969
```
7070

7171
::: warning 注意
72-
`rowIndex` 是数据数组的索引,不是页码索引。删除行后,后续行的索引会前移。
72+
`rowIndex` 是数据数组的索引,不是页码索引。
7373
:::
7474

7575
### moveUp(rowIndex)
@@ -100,6 +100,35 @@ tableRef.current?.moveDown(0); // 第一行下移到第二行
100100
const currentData = tableRef.current?.getData();
101101
```
102102

103+
### insertRow(rowIndex, defaults?)
104+
105+
在指定位置插入一行。
106+
107+
```tsx
108+
tableRef.current?.insertRow(1, { id: 'new', name: '插入行', age: 0 }); // 在第二行位置插入
109+
```
110+
111+
### updateRow(rowIndex, updates)
112+
113+
部分更新指定行数据,不影响其他字段。
114+
115+
```tsx
116+
tableRef.current?.updateRow(0, { name: '新名字' }); // 只更新第一行的 name
117+
```
118+
119+
### validateAll()
120+
121+
手动触发全量校验,返回校验结果。
122+
123+
```tsx
124+
const { isValid, errors } = tableRef.current?.validateAll();
125+
if (isValid) {
126+
// 校验通过
127+
} else {
128+
console.log(errors); // { 'rowId-fieldName': '错误信息' }
129+
}
130+
```
131+
103132
## 配合列固定使用
104133

105134
行操作按钮通常放在固定列中,确保滚动时始终可见:

packages/editable-table/src/components/EditableTable/EditableTable.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494

9595
.et-body::-webkit-scrollbar {
9696
width: 6px;
97+
height: 4px;
9798
}
9899

99100
.et-body::-webkit-scrollbar-thumb {

packages/editable-table/src/components/EditableTable/index.tsx

Lines changed: 30 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ function EditableTable<T extends object = Record<string, unknown>>(
3232
const originalDataRef = useRef<Map<string, T>>(new Map());
3333
const dataRef = useRef<T[]>([]);
3434
dataRef.current = data;
35+
const mountedRef = useRef(true);
36+
useEffect(() => () => { mountedRef.current = false; }, []);
3537

3638
const getColKey = useCallback((col: EditableColumn<T>, idx: number) => {
3739
if (col.key) return col.key;
@@ -59,15 +61,16 @@ function EditableTable<T extends object = Record<string, unknown>>(
5961
// ===== 校验 =====
6062
const validateAll = useCallback((): ValidateAllResult => {
6163
const allErrors: Record<string, string> = {};
62-
data.forEach((row, rowIndex) => {
64+
data.forEach((row) => {
65+
const rowId = String(row[rowKey]);
6366
const rowErrors = validateRow(row, columns);
6467
for (const [dataIndex, msg] of Object.entries(rowErrors)) {
65-
allErrors[`${rowIndex}-${dataIndex}`] = msg;
68+
allErrors[`${rowId}-${dataIndex}`] = msg;
6669
}
6770
});
6871
setErrors(allErrors);
6972
return { isValid: Object.keys(allErrors).length === 0, errors: allErrors };
70-
}, [data, columns]);
73+
}, [data, columns, rowKey]);
7174

7275
// ===== 通过 ref 暴露操作方法 =====
7376
useImperativeHandle(
@@ -84,18 +87,6 @@ function EditableTable<T extends object = Record<string, unknown>>(
8487
removeRow: (rowIndex) => {
8588
setData((prev) => {
8689
const next = prev.filter((_, i) => i !== rowIndex);
87-
// 重新映射错误索引
88-
setErrors((errs) => {
89-
const nextErrs: Record<string, string> = {};
90-
for (const [k, v] of Object.entries(errs)) {
91-
const dashIdx = k.indexOf('-');
92-
const ri = Number(k.slice(0, dashIdx));
93-
const rest = k.slice(dashIdx);
94-
if (ri < rowIndex) nextErrs[k] = v;
95-
else if (ri > rowIndex) nextErrs[`${ri - 1}${rest}`] = v;
96-
}
97-
return nextErrs;
98-
});
9990
onChange?.(next);
10091
return next;
10192
});
@@ -108,18 +99,6 @@ function EditableTable<T extends object = Record<string, unknown>>(
10899
onChange?.(next);
109100
return next;
110101
});
111-
setErrors((prev) => {
112-
const next: Record<string, string> = {};
113-
for (const [k, v] of Object.entries(prev)) {
114-
const dashIdx = k.indexOf('-');
115-
const ri = Number(k.slice(0, dashIdx));
116-
const rest = k.slice(dashIdx);
117-
if (ri === rowIndex - 1) next[`${rowIndex}${rest}`] = v;
118-
else if (ri === rowIndex) next[`${rowIndex - 1}${rest}`] = v;
119-
else next[k] = v;
120-
}
121-
return next;
122-
});
123102
},
124103
moveDown: (rowIndex) => {
125104
if (rowIndex >= dataRef.current.length - 1) return;
@@ -129,18 +108,6 @@ function EditableTable<T extends object = Record<string, unknown>>(
129108
onChange?.(next);
130109
return next;
131110
});
132-
setErrors((prev) => {
133-
const next: Record<string, string> = {};
134-
for (const [k, v] of Object.entries(prev)) {
135-
const dashIdx = k.indexOf('-');
136-
const ri = Number(k.slice(0, dashIdx));
137-
const rest = k.slice(dashIdx);
138-
if (ri === rowIndex) next[`${rowIndex + 1}${rest}`] = v;
139-
else if (ri === rowIndex + 1) next[`${rowIndex}${rest}`] = v;
140-
else next[k] = v;
141-
}
142-
return next;
143-
});
144111
},
145112
getData: () => dataRef.current,
146113
validateAll,
@@ -158,17 +125,6 @@ function EditableTable<T extends object = Record<string, unknown>>(
158125
setData((prev) => {
159126
const next = [...prev];
160127
next.splice(rowIndex, 0, newRow);
161-
setErrors((errs) => {
162-
const nextErrs: Record<string, string> = {};
163-
for (const [k, v] of Object.entries(errs)) {
164-
const dashIdx = k.indexOf('-');
165-
const ri = Number(k.slice(0, dashIdx));
166-
const rest = k.slice(dashIdx);
167-
if (ri < rowIndex) nextErrs[k] = v;
168-
else nextErrs[`${ri + 1}${rest}`] = v;
169-
}
170-
return nextErrs;
171-
});
172128
onChange?.(next);
173129
return next;
174130
});
@@ -201,6 +157,7 @@ function EditableTable<T extends object = Record<string, unknown>>(
201157
const rowId = String(currentRow[rowKey]);
202158
linkedResult.then((updates) => {
203159
if (!updates) return;
160+
if (!mountedRef.current) return;
204161
setData((prev) => {
205162
const idx = prev.findIndex((r) => String(r[rowKey]) === rowId);
206163
if (idx === -1) return prev;
@@ -209,6 +166,8 @@ function EditableTable<T extends object = Record<string, unknown>>(
209166
onChange?.(next);
210167
return next;
211168
});
169+
}).catch((err) => {
170+
console.error('[EditableTable] onFieldChange error', err);
212171
});
213172
} else if (linkedResult) {
214173
setData((prev) => {
@@ -225,7 +184,8 @@ function EditableTable<T extends object = Record<string, unknown>>(
225184
const row = dataRef.current[rowIndex];
226185
const updatedRow = { ...row, [dataIndex]: value };
227186
const err = validateValue(value, updatedRow, col.rules);
228-
const key = `${rowIndex}-${dataIndex}`;
187+
const rowId = String(row[rowKey]);
188+
const key = `${rowId}-${dataIndex}`;
229189
setErrors((prev) => {
230190
const next = { ...prev };
231191
if (err) next[key] = err;
@@ -249,7 +209,7 @@ function EditableTable<T extends object = Record<string, unknown>>(
249209
// 已在编辑中则不覆盖快照,保留最初的原始数据
250210
if (editingRows.has(id)) return;
251211
const row = data.find((r) => String(r[rowKey]) === id);
252-
if (row) originalDataRef.current.set(id, { ...row });
212+
if (row) originalDataRef.current.set(id, typeof structuredClone === 'function' ? structuredClone(row) : { ...row });
253213
setEditingRows((prev) => new Set(prev).add(id));
254214
},
255215
[data, rowKey, editingRows],
@@ -262,12 +222,12 @@ function EditableTable<T extends object = Record<string, unknown>>(
262222
const rowErrors = validateRow(data[rowIndex], columns);
263223
const rowKeyErrors: Record<string, string> = {};
264224
for (const [dataIndex, msg] of Object.entries(rowErrors)) {
265-
rowKeyErrors[`${rowIndex}-${dataIndex}`] = msg;
225+
rowKeyErrors[`${id}-${dataIndex}`] = msg;
266226
}
267227
setErrors((prev) => {
268228
const next: Record<string, string> = {};
269229
for (const [k, v] of Object.entries(prev)) {
270-
if (!k.startsWith(`${rowIndex}-`)) next[k] = v;
230+
if (!k.startsWith(`${id}-`)) next[k] = v;
271231
}
272232
Object.assign(next, rowKeyErrors);
273233
return next;
@@ -297,15 +257,13 @@ function EditableTable<T extends object = Record<string, unknown>>(
297257
});
298258
}
299259
originalDataRef.current.delete(id);
300-
if (rowIndex !== -1) {
301-
setErrors((prev) => {
302-
const n: Record<string, string> = {};
303-
for (const [k, v] of Object.entries(prev)) {
304-
if (!k.startsWith(`${rowIndex}-`)) n[k] = v;
305-
}
306-
return n;
307-
});
308-
}
260+
setErrors((prev) => {
261+
const n: Record<string, string> = {};
262+
for (const [k, v] of Object.entries(prev)) {
263+
if (!k.startsWith(`${id}-`)) n[k] = v;
264+
}
265+
return n;
266+
});
309267
setEditingRows((prev) => {
310268
const n = new Set(prev);
311269
n.delete(id);
@@ -343,7 +301,7 @@ function EditableTable<T extends object = Record<string, unknown>>(
343301
const cols = columns.map((col) => {
344302
if (col.width) {
345303
const w = typeof col.width === 'number' ? col.width : parseInt(col.width as string, 10) || 150;
346-
return `minmax(${w}px, ${w}fr)`;
304+
return `minmax(${w}px, 1fr)`;
347305
}
348306
return 'minmax(150px, 1fr)';
349307
}).join(' ');
@@ -359,22 +317,22 @@ function EditableTable<T extends object = Record<string, unknown>>(
359317
return 150;
360318
});
361319

362-
// 从左往右累计 left fixed
320+
// 从左往右累计 left fixed(只累加已标记 fixed='left' 的列宽)
363321
let leftAcc = 0;
364322
for (let i = 0; i < columns.length; i++) {
365323
if (columns[i].fixed === 'left') {
366324
offsets[i] = { left: leftAcc };
325+
leftAcc += widths[i];
367326
}
368-
leftAcc += widths[i];
369327
}
370328

371-
// 从右往左累计 right fixed
329+
// 从右往左累计 right fixed(只累加已标记 fixed='right' 的列宽)
372330
let rightAcc = 0;
373331
for (let i = columns.length - 1; i >= 0; i--) {
374332
if (columns[i].fixed === 'right') {
375333
offsets[i] = { right: rightAcc };
334+
rightAcc += widths[i];
376335
}
377-
rightAcc += widths[i];
378336
}
379337

380338
return offsets;
@@ -492,7 +450,7 @@ function EditableTable<T extends object = Record<string, unknown>>(
492450
? col.editable !== false
493451
: isEditing && col.editable !== false;
494452
const errorKey = colDataIndex
495-
? `${virtualRow.index}-${colDataIndex}`
453+
? `${id}-${colDataIndex}`
496454
: undefined;
497455
const offset = fixedOffsets[colIndex];
498456
const isFixed = col.fixed === 'left' || col.fixed === 'right';
@@ -583,6 +541,8 @@ function EditableTable<T extends object = Record<string, unknown>>(
583541
style={{
584542
display: 'grid',
585543
gridTemplateColumns: gridTemplate,
544+
width: 'fit-content',
545+
minWidth: '100%',
586546
}}
587547
>
588548
{columns.map((col, colIndex) => {
@@ -594,7 +554,7 @@ function EditableTable<T extends object = Record<string, unknown>>(
594554
editableMode === 'all'
595555
? col.editable !== false
596556
: isEditing && col.editable !== false;
597-
const errorKey = colDataIndex ? `${rowIndex}-${colDataIndex}` : undefined;
557+
const errorKey = colDataIndex ? `${id}-${colDataIndex}` : undefined;
598558
const offset = fixedOffsets[colIndex];
599559
const isFixed = col.fixed === 'left' || col.fixed === 'right';
600560

0 commit comments

Comments
 (0)