Files
contract-manager/docs/task/table_cell_implementation_guide.md
songqq 09b0da498b feat(service): 实现国际化支持并优化Service层
重构文件类型相关Service以支持国际化查询
添加findOneByLang辅助方法统一查询逻辑
实现StringConverter支持UI控件显示
优化缓存配置和查询性能
新增UnitStringConverter和CustomerCatalogStringConverter
完善文档和测试用例
2025-09-24 16:20:49 +08:00

6.4 KiB
Raw Blame History

TableCell 实现规则与模式指南

1. 目的与作用

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

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

2. 文件结构与命名规范

  • 文件位置client/src/main/java/com/ecep/contract/controller/table/cell/
  • 命名规范:使用 PascalCase驼峰命名法首字母大写TableCell 结尾
    • 示例:CompanyTableCell.javaLocalDateFieldTableCell.java

3. TableCell 分类与实现模式

3.1 基础 TableCell

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

实现规则

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

示例

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() 方法自定义实体显示格式

示例

@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() 方法中处理编辑状态的显示
  • 提供编辑控件和事件处理

示例

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() 方法格式化实体
  • NamedEntityBasedEntity 有特殊处理
  • 允许子类重写 format() 方法自定义格式

4.3 服务获取方式

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

5. 特殊情况处理

5.1 非 Integer 类型 ID

当实体 ID 不是 Integer 类型时,需要重写 initialize() 方法:

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

5.2 自定义显示格式

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

@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.javaLocalDateFieldTableCell.java

9.2 实体引用 TableCell

参考 CompanyTableCell.javaContractTableCell.java

9.3 特殊处理 TableCell

参考 ContractFileTypeTableCell.java