重构所有VO对象,统一字段命名规范,移除冗余字段,优化接口实现 新增Voable接口用于VO对象转换 调整BaseViewModel和ProjectBasedViewModel接口定义 更新相关服务和控制器以适应VO对象变更
339 lines
10 KiB
Java
339 lines
10 KiB
Java
package com.ecep.contract.controller;
|
|
|
|
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 com.ecep.contract.util.FxmlPath;
|
|
import com.ecep.contract.util.FxmlUtils;
|
|
import com.ecep.contract.vo.EmployeeVo;
|
|
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.Desktop;
|
|
import com.ecep.contract.SpringApp;
|
|
import com.ecep.contract.model.Employee;
|
|
import com.ecep.contract.model.IdentityEntity;
|
|
import com.ecep.contract.service.EmployeeService;
|
|
import com.ecep.contract.service.SysConfService;
|
|
import com.ecep.contract.util.UITools;
|
|
import com.ecep.contract.vm.CurrentEmployee;
|
|
import com.ecep.contract.vm.IdentityViewModel;
|
|
|
|
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;
|
|
|
|
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;
|
|
private EmployeeVo currentUser;
|
|
|
|
private HashMap<Class<?>, Object> cachedBeans = new HashMap<>();
|
|
|
|
public <T> T getBean(Class<T> requiredType) throws BeansException {
|
|
return SpringApp.getBean(requiredType);
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
public <T> T getCachedBean(Class<T> requiredType) throws BeansException {
|
|
Object object = cachedBeans.get(requiredType);
|
|
if (object == null) {
|
|
object = getBean(requiredType);
|
|
cachedBeans.put(requiredType, object);
|
|
}
|
|
return (T) object;
|
|
}
|
|
|
|
public SysConfService getConfService() {
|
|
return getCachedBean(SysConfService.class);
|
|
}
|
|
|
|
public EmployeeService getEmployeeService() {
|
|
return getCachedBean(EmployeeService.class);
|
|
}
|
|
|
|
public EmployeeVo getCurrentUser() {
|
|
if (currentUser == null) {
|
|
currentUser = getEmployeeService().findById(Desktop.instance.getActiveEmployeeId());
|
|
}
|
|
return currentUser;
|
|
}
|
|
|
|
public CurrentEmployee getCurrentEmployee() {
|
|
return Desktop.instance.getActiveEmployee();
|
|
}
|
|
|
|
public Locale getLocale() {
|
|
CurrentEmployee currentEmployee = getCurrentEmployee();
|
|
if (currentEmployee == null) {
|
|
return Locale.getDefault();
|
|
}
|
|
return currentEmployee.localeProperty().get();
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|