Browse Source

feat(table-cells): Improve cell alignment and preserve styles during merge/split operations

- Change default vertical alignment from top to middle in table cells
- Add center text alignment to table cell styling
- Extend CellStyleOverrides type to preserve original styles in merged cells
- Update mergeCells function to capture and store cell styles alongside content
- Modify splitCell function to restore both content and original styles when unmerging
- Create cell data map to track text and style for each merged cell position
- Apply original styles to recovered cells after split operation
- Update cellStyleToCSS utility to enforce center text alignment by default
- Enhance JSDoc comments to reflect style preservation in merge/split workflows
Zhang Yice 1 month ago
parent
commit
c3297c01ee

+ 2 - 1
src/components/Editor/blocks/TableCell.css

@@ -5,7 +5,8 @@
5 5
 .table-cell {
6 6
   border: 1px solid #d9d9d9;
7 7
   padding: 8px 12px;
8
-  vertical-align: top;
8
+  vertical-align: middle;
9
+  text-align: center;
9 10
   position: relative;
10 11
   transition: all 0.2s ease;
11 12
 }

+ 1 - 0
src/components/Editor/blocks/TableCell.tsx

@@ -93,6 +93,7 @@ export const TableCell: React.FC<TableCellProps> = ({
93 93
         baseStyle={{
94 94
           fontSize: '12px',
95 95
           lineHeight: 1.5,
96
+          textAlign: 'center', // 强制居中对齐
96 97
         }}
97 98
       />
98 99
     </td>

+ 2 - 1
src/types/editor.ts

@@ -104,11 +104,12 @@ export interface CellStyleOverrides extends StyleOverrides {
104 104
   border_color?: string;
105 105
   border_width?: number;
106 106
   vertical_align?: 'top' | 'center' | 'bottom';
107
-  // 合并单元格时保存的原始数据
107
+  // 合并单元格时保存的原始数据(包含text和style)
108 108
   _mergedCells?: Array<{
109 109
     rowOffset: number;
110 110
     colOffset: number;
111 111
     text: string | RichText[];
112
+    style?: CellStyleOverrides; // 保存原始样式
112 113
   }>;
113 114
 }
114 115
 

+ 27 - 19
src/utils/blockOperations.ts

@@ -241,7 +241,7 @@ export function deleteTableColumn(table: TableBlock, colIndex: number): TableBlo
241 241
  * 合并单元格
242 242
  * 
243 243
  * 支持横向合并(colspan)和纵向合并(rowspan)
244
- * 合并时会保存所有被合并单元格的原始内容,以便拆分时恢复
244
+ * 合并时会保存所有被合并单元格的原始内容和样式,以便拆分时恢复
245 245
  * 
246 246
  * @param table 表格块
247 247
  * @param startRow 起始行
@@ -254,11 +254,11 @@ export function deleteTableColumn(table: TableBlock, colIndex: number): TableBlo
254 254
  * ```ts
255 255
  * // 横向合并: a + b (第0行,第0-1列)
256 256
  * mergeCells(table, 0, 0, 0, 1);
257
- * // 结果: a单元格 colspan=2, 保存b的内容到 _mergedCells
257
+ * // 结果: a单元格 colspan=2, 保存b的内容和样式到 _mergedCells
258 258
  * 
259 259
  * // 纵向合并: a + d (第0-1行,第0列)
260 260
  * mergeCells(table, 0, 0, 1, 0);
261
- * // 结果: a单元格 rowspan=2, 保存d的内容到 _mergedCells
261
+ * // 结果: a单元格 rowspan=2, 保存d的内容和样式到 _mergedCells
262 262
  * ```
263 263
  */
264 264
 export function mergeCells(
@@ -268,11 +268,12 @@ export function mergeCells(
268 268
   endRow: number,
269 269
   endCol: number
270 270
 ): TableBlock {
271
-  // 收集所有被合并单元格的内容(保存位置偏移量)
271
+  // 收集所有被合并单元格的内容和样式(保存位置偏移量)
272 272
   const mergedCells: Array<{
273 273
     rowOffset: number;
274 274
     colOffset: number;
275 275
     text: string | RichText[];
276
+    style: any; // 保存原始样式
276 277
   }> = [];
277 278
   
278 279
   // 收集主单元格内容用于显示
@@ -286,11 +287,12 @@ export function mergeCells(
286 287
             ? cell.text 
287 288
             : cell.text.map(seg => seg.text).join('');
288 289
           
289
-          // 保存所有单元格的原始数据(包括主单元格)
290
+          // 保存所有单元格的原始数据和样式(包括主单元格)
290 291
           mergedCells.push({
291 292
             rowOffset: rowIdx - startRow,
292 293
             colOffset: colIdx - startCol,
293 294
             text: cell.text,
295
+            style: { ...cell.style }, // 深拷贝样式
294 296
           });
295 297
           
296 298
           // 用于显示的文本
@@ -321,7 +323,7 @@ export function mergeCells(
321 323
           colspan: endCol - startCol + 1,
322 324
           style: {
323 325
             ...cell.style,
324
-            _mergedCells: mergedCells, // 保存所有单元格的原始内容
326
+            _mergedCells: mergedCells, // 保存所有单元格的原始内容和样式
325 327
           },
326 328
         };
327 329
       }
@@ -347,7 +349,7 @@ export function mergeCells(
347 349
 /**
348 350
  * 拆分单元格
349 351
  * 
350
- * 将已合并的单元格拆分回独立单元格,并恢复原始内容到对应位置
352
+ * 将已合并的单元格拆分回独立单元格,并恢复原始内容和样式到对应位置
351 353
  * 
352 354
  * @param table 表格块
353 355
  * @param rowIndex 单元格所在行
@@ -358,11 +360,11 @@ export function mergeCells(
358 360
  * ```ts
359 361
  * // 拆分横向合并的单元格 (a+b)
360 362
  * splitCell(table, 0, 0);
361
- * // 结果: a单元格恢复为普通单元格(内容为原始a), b单元格恢复(内容为原始b)
363
+ * // 结果: a单元格恢复为普通单元格(内容为原始a,样式为原始a样式), b单元格恢复(内容为原始b,样式为原始b样式)
362 364
  * 
363 365
  * // 拆分纵向合并的单元格 (a+d)  
364 366
  * splitCell(table, 0, 0);
365
- * // 结果: a单元格恢复为普通单元格(内容为原始a), d单元格恢复(内容为原始d)
367
+ * // 结果: a单元格恢复为普通单元格(内容为原始a,样式为原始a样式), d单元格恢复(内容为原始d,样式为原始d样式)
366 368
  * ```
367 369
  */
368 370
 export function splitCell(
@@ -384,14 +386,14 @@ export function splitCell(
384 386
   const rowspan = targetCell.rowspan || 1;
385 387
   const colspan = targetCell.colspan || 1;
386 388
   
387
-  // 获取保存的原始单元格数据
389
+  // 获取保存的原始单元格数据(包含text和style)
388 390
   const mergedCells = targetCell.style._mergedCells || [];
389 391
   
390
-  // 创建一个映射,用于快速查找原始内容
391
-  const cellDataMap = new Map<string, string | RichText[]>();
392
-  mergedCells.forEach(({ rowOffset, colOffset, text }) => {
392
+  // 创建一个映射,用于快速查找原始内容和样式
393
+  const cellDataMap = new Map<string, { text: string | RichText[]; style: any }>();
394
+  mergedCells.forEach(({ rowOffset, colOffset, text, style }) => {
393 395
     const key = `${rowOffset}-${colOffset}`;
394
-    cellDataMap.set(key, text);
396
+    cellDataMap.set(key, { text, style: style || {} });
395 397
   });
396 398
   
397 399
   const rows = table.content.rows.map((row, rowIdx) => {
@@ -411,12 +413,14 @@ export function splitCell(
411 413
       const colOffset = colIdx - colIndex;
412 414
       const key = `${rowOffset}-${colOffset}`;
413 415
       
414
-      // 从保存的数据中恢复原始内容
415
-      const originalText = cellDataMap.get(key) || '';
416
+      // 从保存的数据中恢复原始内容和样式
417
+      const cellData = cellDataMap.get(key);
418
+      const originalText = cellData?.text || '';
419
+      const originalStyle = cellData?.style || {};
416 420
       
417 421
       // 主单元格:恢复为普通单元格,清除合并标记
418 422
       if (rowIdx === rowIndex && colIdx === colIndex) {
419
-        const newStyle = { ...cell.style };
423
+        const newStyle = { ...originalStyle };
420 424
         delete newStyle._mergedCells; // 清除保存的合并数据
421 425
         
422 426
         return {
@@ -424,14 +428,18 @@ export function splitCell(
424 428
           text: originalText,
425 429
           rowspan: 1,
426 430
           colspan: 1,
427
-          style: newStyle,
431
+          style: newStyle, // 使用原始样式
428 432
         };
429 433
       }
430 434
       
431
-      // 被合并的单元格:恢复为独立单元格,并恢复原始内容
435
+      // 被合并的单元格:恢复为独立单元格,并恢复原始内容和样式
436
+      const recoveredStyle = { ...originalStyle };
437
+      delete recoveredStyle._mergedCells;
438
+      
432 439
       return {
433 440
         ...createEmptyCell(),
434 441
         text: originalText,
442
+        style: recoveredStyle, // 使用原始样式
435 443
       };
436 444
     });
437 445
     

+ 7 - 1
src/utils/styleResolver.ts

@@ -144,7 +144,13 @@ export function cellStyleToCSS(style: CellStyleOverrides): React.CSSProperties {
144 144
   if (style.background_color) css.backgroundColor = `#${style.background_color}`;
145 145
   if (style.border_color) css.borderColor = `#${style.border_color}`;
146 146
   if (style.border_width) css.borderWidth = `${style.border_width}px`;
147
-  if (style.vertical_align) css.verticalAlign = style.vertical_align;
147
+  
148
+  // 垂直对齐:默认居中
149
+  css.verticalAlign = style.vertical_align || 'middle';
150
+  
151
+  // 水平对齐:强制覆盖为居中(忽略单元格原有的 align 样式)
152
+  // 注释:如果希望保留原有的对齐方式,可以改为:css.textAlign = style.align || 'center';
153
+  css.textAlign = 'center';
148 154
   
149 155
   return css;
150 156
 }