# TableCell 实现规则与模式指南

## 1. 目的与作用

TableCell 是 JavaFX TableView 组件中的关键元素，负责表格中单个单元格的渲染和交互。在 Contract-Manager 项目中，TableCell 主要用于：

- 显示各种类型的数据（数字、日期、实体引用等）
- 提供自定义的显示格式和样式
- 支持异步加载关联数据
- 实现单元格内编辑功能

## 2. 文件结构与命名规范

- **文件位置**：`client/src/main/java/com/ecep/contract/controller/table/cell/`
- **命名规范**：使用 PascalCase（驼峰命名法，首字母大写），以 `TableCell` 结尾
  - 示例：`CompanyTableCell.java`、`LocalDateFieldTableCell.java`

## 3. TableCell 分类与实现模式

### 3.1 基础 TableCell

直接继承 JavaFX 的 `TableCell` 类，适用于简单的数据显示。

**实现规则**：
- 继承 `TableCell<S, T>`，其中 S 是表格行数据类型，T 是单元格数据类型
- 实现 `updateItem(T item, boolean empty)` 方法
- 提供静态的 `forTableColumn()` 工厂方法

**示例**：
```java
public class NumberTableCell<S> extends TableCell<S, Number> {
    private final NumberStringConverter numberStringConverter;

    public NumberTableCell(NumberStringConverter numberStringConverter) {
        this.numberStringConverter = numberStringConverter;
    }

    @Override
    protected void updateItem(Number item, boolean empty) {
        super.updateItem(item, empty);
        if (empty || item == null) {
            setText(null);
        } else {
            setText(numberStringConverter.toString(item));
        }
    }

    public static <S> Callback<TableColumn<S, Number>, TableCell<S, Number>> forTableColumn(NumberStringConverter numberStringConverter) {
        return param -> new NumberTableCell<>(numberStringConverter);
    }
}
```

### 3.2 异步更新 TableCell

继承 `AsyncUpdateTableCell`，用于异步加载并显示关联的实体数据。

**实现规则**：
- 继承 `AsyncUpdateTableCell<V, K, T>`，其中 V 是表格行数据类型，K 是 ID 类型，T 是实体类型
- 提供构造函数接收服务对象，或实现 `getServiceBean()` 方法从 Spring 容器获取服务
- 可选：重写 `initialize()` 方法自定义实体加载逻辑
- 可选：重写 `format()` 方法自定义实体显示格式

**示例**：
```java
@NoArgsConstructor
public class CompanyTableCell<V> extends AsyncUpdateTableCell<V, Integer, CompanyVo> {
    public CompanyTableCell(CompanyService companyService) {
        setService(companyService);
    }

    @Override
    protected CompanyService getServiceBean() {
        return SpringApp.getBean(CompanyService.class);
    }
}
```

### 3.3 可编辑 TableCell

实现单元格内编辑功能，如日期选择、文本编辑等。

**实现规则**：
- 继承 `TableCell` 或其子类
- 实现 `startEdit()`、`cancelEdit()` 方法
- 在 `updateItem()` 方法中处理编辑状态的显示
- 提供编辑控件和事件处理

**示例**：
```java
public class LocalDateFieldTableCell<S> extends TableCell<S, LocalDate> {
    private final DatePicker datePicker;
    
    // 构造函数、converter 属性等
    
    @Override
    public void startEdit() {
        if (isEmpty()) {
            return;
        }
        super.startEdit();
        setGraphic(datePicker);
        datePicker.setConverter(getConverter());
        datePicker.setValue(getItem());
        datePicker.requestFocus();
    }
    
    @Override
    public void cancelEdit() {
        super.cancelEdit();
        LocalDate item = getItem();
        if (item == null) {
            setText(null);
        } else {
            setText(getConverter().toString(item));
        }
        setGraphic(null);
    }
    
    @Override
    protected void updateItem(LocalDate item, boolean empty) {
        // 实现更新逻辑
    }
}
```

## 4. AsyncUpdateTableCell 核心功能

`AsyncUpdateTableCell` 是项目中最常用的基类，提供以下核心功能：

### 4.1 异步加载机制

- 显示占位符（`#id`）直到数据加载完成
- 使用线程池执行异步任务
- 自动取消不再需要的异步任务
- 确保在 JavaFX 应用线程更新 UI

### 4.2 实体显示格式化

- 默认使用 `toString()` 方法格式化实体
- 对 `NamedEntity` 和 `BasedEntity` 有特殊处理
- 允许子类重写 `format()` 方法自定义格式

### 4.3 服务获取方式

- 通过构造函数注入服务
- 或通过 `getServiceBean()` 方法从 Spring 容器获取

## 5. 特殊情况处理

### 5.1 非 Integer 类型 ID

当实体 ID 不是 Integer 类型时，需要重写 `initialize()` 方法：

```java
@Override
protected ContractFileTypeLocalVo initialize() {
    ContractFileType item = getItem();
    ContractFileTypeLocalVo localVo = getServiceBean().findByType(
        Desktop.instance.getActiveEmployee().localeProperty().get(), item);
    return localVo;
}
```

### 5.2 自定义显示格式

当需要自定义实体的显示格式时，重写 `format()` 方法：

```java
@Override
public String format(ContractFileTypeLocalVo entity) {
    if (entity == null) {
        return null;
    }
    return entity.getValue();
}
```

## 6. 工厂方法模式

所有 TableCell 类都应提供静态的 `forTableColumn()` 工厂方法，用于创建单元格工厂回调：

- 无参数版本：`public static <S> Callback<TableColumn<S, T>, TableCell<S, T>> forTableColumn()`
- 带服务参数版本：`public static <S> Callback<TableColumn<S, T>, TableCell<S, T>> forTableColumn(ServiceType service)`

## 7. 代码规范

- 使用 Lombok 注解（如 `@NoArgsConstructor`）简化代码
- 添加适当的 JavaDoc 注释
- 遵循 JavaFX 最佳实践
- 处理空值和异常情况
- 确保线程安全，UI 更新在 JavaFX 应用线程执行

## 8. 实现检查清单

创建新的 TableCell 时，请检查以下项目：

- [ ] 类名符合 `*TableCell` 命名规范
- [ ] 放置在正确的包路径下
- [ ] 选择合适的基类（TableCell、AsyncUpdateTableCell）
- [ ] 实现必要的构造函数
- [ ] 实现或重写 `updateItem()` 方法
- [ ] 提供静态的 `forTableColumn()` 工厂方法
- [ ] 对于异步更新类型，实现 `getServiceBean()` 或提供服务注入
- [ ] 添加适当的注释
- [ ] 处理空值和异常情况

## 9. 示例实现

### 9.1 简单数据类型 TableCell

参考 `NumberTableCell.java`、`LocalDateFieldTableCell.java`

### 9.2 实体引用 TableCell

参考 `CompanyTableCell.java`、`ContractTableCell.java`

### 9.3 特殊处理 TableCell

参考 `ContractFileTypeTableCell.java`