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.ContextUtils; 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 implements ContextUtils { private static final Logger logger = LoggerFactory.getLogger(BaseController.class); public static HashMap stages = new HashMap<>(); public static CompletableFuture show(Class clz, Window owner) { return show(clz, owner, null); } public static CompletableFuture show(Class clz, Window owner, Consumer 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 , T extends BaseController> void show( Class 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) controller).setViewModel(viewModel); } controller.show(loader, owner, Modality.NONE, key); }); } private static 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 shutdown() { if (logger.isDebugEnabled()) { logger.debug("shutdown"); } // 关闭所有窗口 CompletableFuture future = new CompletableFuture<>(); ArrayList 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 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, Object> cachedBeans = new HashMap<>(); public T getBean(Class requiredType) throws BeansException { return SpringApp.getBean(requiredType); } @SuppressWarnings("unchecked") public T getCachedBean(Class 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); } }