新增用友U8配置窗口,支持日期和文本配置项的编辑与保存。实现从CloudInfo到CloudYu的数据迁移任务,优化任务执行方式。重构多个同步任务类继承Tasker基类,统一任务处理逻辑。扩展YongYouU8Service功能,添加配置相关接口。调整UI布局和菜单项,增加配置入口。 refactor: 重命名CompanyTableCell为EmployeeRoleTableCell style: 清理无用导入和格式化代码 fix: 修复ContractTypeSyncTask中分类和方向字段设置错误
327 lines
9.9 KiB
Java
327 lines
9.9 KiB
Java
package com.ecep.contract.manager.ui;
|
|
|
|
import java.beans.PropertyDescriptor;
|
|
import java.lang.reflect.InvocationTargetException;
|
|
import java.lang.reflect.Method;
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.Locale;
|
|
import java.util.concurrent.CompletableFuture;
|
|
import java.util.function.Consumer;
|
|
|
|
import org.apache.logging.log4j.util.Strings;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
import org.springframework.beans.BeanUtils;
|
|
import org.springframework.beans.BeansException;
|
|
|
|
import com.ecep.contract.manager.Desktop;
|
|
import com.ecep.contract.manager.SpringApp;
|
|
import com.ecep.contract.manager.ds.other.model.Employee;
|
|
import com.ecep.contract.manager.ds.other.model.IdentityEntity;
|
|
import com.ecep.contract.manager.ds.other.service.EmployeeService;
|
|
import com.ecep.contract.manager.ds.other.service.SysConfService;
|
|
import com.ecep.contract.manager.ds.other.vo.IdentityViewModel;
|
|
import com.ecep.contract.manager.util.FxmlUtils;
|
|
import com.ecep.contract.manager.util.UITools;
|
|
|
|
import javafx.application.Platform;
|
|
import javafx.beans.property.SimpleStringProperty;
|
|
import javafx.beans.property.StringProperty;
|
|
import javafx.beans.value.ObservableValue;
|
|
import javafx.fxml.FXMLLoader;
|
|
import javafx.scene.Scene;
|
|
import javafx.scene.control.Label;
|
|
import javafx.stage.Modality;
|
|
import javafx.stage.Stage;
|
|
import javafx.stage.Window;
|
|
import javafx.stage.WindowEvent;
|
|
import lombok.Getter;
|
|
import lombok.Setter;
|
|
|
|
public class BaseController {
|
|
private static final Logger logger = LoggerFactory.getLogger(BaseController.class);
|
|
public static HashMap<String, Stage> stages = new HashMap<>();
|
|
|
|
public static <T extends BaseController> CompletableFuture<Void> show(Class<T> clz, Window owner) {
|
|
return show(clz, owner, null);
|
|
}
|
|
|
|
public static <T extends BaseController> CompletableFuture<Void> show(Class<T> clz, Window owner,
|
|
Consumer<T> consumer) {
|
|
String key = clz.getName();
|
|
if (toFront(key)) {
|
|
return null;
|
|
}
|
|
FxmlPath annotation = clz.getAnnotation(FxmlPath.class);
|
|
if (annotation == null) {
|
|
throw new RuntimeException("@FxmlPath is required");
|
|
}
|
|
|
|
return FxmlUtils.newLoaderAsyncWithRunLater(annotation.value(), null, loader -> {
|
|
T controller = loader.getController();
|
|
if (consumer != null) {
|
|
consumer.accept(controller);
|
|
}
|
|
controller.show(loader, owner, Modality.NONE, key);
|
|
});
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
public static <K extends IdentityEntity, M extends IdentityViewModel<K>, T extends BaseController> void show(
|
|
Class<T> clz, M viewModel, Window owner) {
|
|
String key = getKey(clz, viewModel);
|
|
if (toFront(key)) {
|
|
return;
|
|
}
|
|
FxmlPath annotation = clz.getAnnotation(FxmlPath.class);
|
|
if (annotation == null) {
|
|
throw new RuntimeException("@FxmlPath is required");
|
|
}
|
|
|
|
FxmlUtils.newLoaderAsyncWithRunLater(annotation.value(), null, loader -> {
|
|
T controller = loader.getController();
|
|
if (controller instanceof AbstEntityController<?, ?>) {
|
|
((AbstEntityController<?, M>) controller).setViewModel(viewModel);
|
|
}
|
|
controller.show(loader, owner, Modality.NONE, key);
|
|
});
|
|
}
|
|
|
|
private static <V> String getKey(Class<?> clz, V viewModel) {
|
|
PropertyDescriptor idProperty = BeanUtils.getPropertyDescriptor(viewModel.getClass(), "id");
|
|
if (idProperty != null) {
|
|
Method readMethod = idProperty.getReadMethod();
|
|
if (readMethod != null) {
|
|
try {
|
|
Object object = readMethod.invoke(viewModel);
|
|
if (object instanceof ObservableValue<?> value) {
|
|
return clz.getName() + "#" + value.getValue();
|
|
}
|
|
} catch (IllegalAccessException | InvocationTargetException ignored) {
|
|
}
|
|
}
|
|
}
|
|
return clz.getName() + "#" + viewModel.hashCode();
|
|
}
|
|
|
|
public static void show(Class<?> clz, String resource, Window owner) {
|
|
String key = clz.getName();
|
|
if (toFront(key)) {
|
|
return;
|
|
}
|
|
FxmlUtils.newLoaderAsyncWithRunLater(resource, null, loader -> {
|
|
BaseController controller = loader.getController();
|
|
controller.show(loader, owner, Modality.NONE, key);
|
|
});
|
|
}
|
|
|
|
public static boolean toFront(String key) {
|
|
Stage stage = stages.get(key);
|
|
if (stage != null) {
|
|
stage.toFront();
|
|
if (!stage.isShowing()) {
|
|
stage.show();
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public static CompletableFuture<Void> shutdown() {
|
|
if (logger.isDebugEnabled()) {
|
|
logger.debug("shutdown");
|
|
}
|
|
// 关闭所有窗口
|
|
CompletableFuture<Void> future = new CompletableFuture<>();
|
|
ArrayList<Stage> list = new ArrayList<>(stages.values());
|
|
if (list.isEmpty()) {
|
|
if (logger.isDebugEnabled()) {
|
|
logger.debug("no stage should to close.");
|
|
}
|
|
future.complete(null);
|
|
} else {
|
|
if (logger.isDebugEnabled()) {
|
|
logger.debug("try to close stages {}.", list);
|
|
}
|
|
Platform.runLater(() -> {
|
|
try {
|
|
for (Stage stage : list) {
|
|
System.out.println("stage = " + stage);
|
|
stage.close();
|
|
if (logger.isDebugEnabled()) {
|
|
logger.debug("stage close {}", stage);
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
logger.error("stage close error", e);
|
|
}
|
|
future.complete(null);
|
|
});
|
|
}
|
|
return future;
|
|
}
|
|
|
|
public CompletableFuture<Void> runAsync(Runnable runnable) {
|
|
return CompletableFuture.runAsync(runnable, Desktop.instance.getExecutorService()).exceptionally(this::handleException);
|
|
}
|
|
|
|
/**
|
|
* 窗口标题
|
|
*/
|
|
@Getter
|
|
private final StringProperty title = new SimpleStringProperty("窗口");
|
|
/**
|
|
* 窗口是否已经请求关闭
|
|
*/
|
|
@Getter
|
|
private boolean closeRequested = false;
|
|
/**
|
|
* 用于管理窗口的Key
|
|
*/
|
|
private String stageKey;
|
|
public Label leftStatusLabel;
|
|
public Label rightStatusLabel;
|
|
|
|
@Getter
|
|
@Setter
|
|
private Locale locale = Locale.getDefault();
|
|
|
|
@Setter
|
|
private SysConfService confService;
|
|
@Setter
|
|
private EmployeeService employeeService;
|
|
private Employee currentUser;
|
|
|
|
protected <T> T getBean(Class<T> requiredType) throws BeansException {
|
|
return SpringApp.getBean(requiredType);
|
|
}
|
|
|
|
public SysConfService getConfService() {
|
|
if (confService == null) {
|
|
confService = getBean(SysConfService.class);
|
|
}
|
|
return confService;
|
|
}
|
|
|
|
public EmployeeService getEmployeeService() {
|
|
if (employeeService == null) {
|
|
employeeService = getBean(EmployeeService.class);
|
|
}
|
|
return employeeService;
|
|
}
|
|
|
|
public Employee getCurrentUser() {
|
|
if (currentUser == null) {
|
|
currentUser = getEmployeeService().findById(Desktop.instance.getActiveEmployeeId());
|
|
}
|
|
return currentUser;
|
|
}
|
|
|
|
public String getMessage(String code, Object... args) {
|
|
return SpringApp.getMessage(code, args, getLocale());
|
|
}
|
|
|
|
protected void setStatus() {
|
|
setStatus(Strings.EMPTY);
|
|
}
|
|
|
|
public void setStatus(String status) {
|
|
if (leftStatusLabel == null) {
|
|
return;
|
|
}
|
|
Platform.runLater(() -> leftStatusLabel.textProperty().set(status));
|
|
}
|
|
|
|
public void setRightStatus(String status) {
|
|
if (rightStatusLabel == null) {
|
|
return;
|
|
}
|
|
Platform.runLater(() -> rightStatusLabel.textProperty().set(status));
|
|
}
|
|
|
|
/**
|
|
* FXML loader 载入时会自动运行此函数
|
|
*/
|
|
public void initialize() {
|
|
|
|
}
|
|
|
|
public void show(Stage stage) {
|
|
stage.titleProperty().bind(title);
|
|
stage.setOnShown(this::onShown);
|
|
stage.setOnShowing(this::onShowing);
|
|
stage.setOnCloseRequest(this::onCloseRequest);
|
|
stage.setOnHiding(this::onHiding);
|
|
stage.setOnHidden(this::onHidden);
|
|
stage.show();
|
|
}
|
|
|
|
public Stage show(FXMLLoader loader, Window owner, Modality modality) {
|
|
Scene scene = new Scene(loader.getRoot());
|
|
Stage stage = new Stage();
|
|
if (owner != null) {
|
|
stage.initOwner(owner);
|
|
stage.initModality(modality);
|
|
}
|
|
stage.setScene(scene);
|
|
show(stage);
|
|
|
|
return stage;
|
|
}
|
|
|
|
protected Stage show(FXMLLoader loader, Window window, Modality modality, String stageKey) {
|
|
Stage stage = show(loader, window, modality);
|
|
stages.put(stageKey, stage);
|
|
this.stageKey = stageKey;
|
|
return stage;
|
|
}
|
|
|
|
public void onShowing(WindowEvent windowEvent) {
|
|
|
|
}
|
|
|
|
public void onShown(WindowEvent windowEvent) {
|
|
setStatus("");
|
|
setRightStatus("");
|
|
}
|
|
|
|
/**
|
|
* 当窗口要关闭前
|
|
*
|
|
* @param windowEvent event
|
|
*/
|
|
public void onCloseRequest(WindowEvent windowEvent) {
|
|
closeRequested = true;
|
|
}
|
|
|
|
public void onHiding(WindowEvent windowEvent) {
|
|
|
|
}
|
|
|
|
public void onHidden(WindowEvent windowEvent) {
|
|
if (stageKey != null) {
|
|
stages.remove(stageKey);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* 处理错误
|
|
*
|
|
* @param ex 错误信息
|
|
* @return Void
|
|
*/
|
|
public Void handleException(Throwable ex) {
|
|
handleException(ex.getMessage(), ex);
|
|
return null;
|
|
}
|
|
|
|
public void handleException(String message, Throwable ex) {
|
|
if (logger.isErrorEnabled()) {
|
|
logger.error(message, ex);
|
|
}
|
|
UITools.showExceptionAndWait(message, ex);
|
|
}
|
|
}
|