refactor: 重构查询服务使用ParamConstant替换ServiceConstant style: 清理无用代码和注释 fix: 修复CompanyCustomerEvaluationFormFileService查询方法 docs: 更新CloudYuVo和CompanyBankAccountVo字段注释
385 lines
14 KiB
Java
385 lines
14 KiB
Java
package com.ecep.contract.util;
|
||
|
||
import java.io.PrintWriter;
|
||
import java.io.StringWriter;
|
||
import java.util.List;
|
||
import java.util.concurrent.CompletableFuture;
|
||
import java.util.concurrent.ExecutorService;
|
||
import java.util.concurrent.Executors;
|
||
import java.util.function.Consumer;
|
||
import java.util.function.Function;
|
||
|
||
import com.ecep.contract.model.IdentityEntity;
|
||
import com.ecep.contract.service.QueryService;
|
||
import com.ecep.contract.vm.IdentityViewModel;
|
||
import javafx.beans.property.ObjectProperty;
|
||
import org.controlsfx.control.textfield.AutoCompletionBinding;
|
||
import org.controlsfx.control.textfield.TextFields;
|
||
import org.slf4j.Logger;
|
||
import org.slf4j.LoggerFactory;
|
||
import org.springframework.util.StringUtils;
|
||
|
||
import com.ecep.contract.Desktop;
|
||
import com.ecep.contract.Message;
|
||
import com.ecep.contract.converter.EntityStringConverter;
|
||
import com.ecep.contract.task.Tasker;
|
||
|
||
import javafx.application.Platform;
|
||
import javafx.beans.property.SimpleObjectProperty;
|
||
import javafx.collections.FXCollections;
|
||
import javafx.collections.ObservableList;
|
||
import javafx.geometry.Insets;
|
||
import javafx.geometry.Rectangle2D;
|
||
import javafx.scene.Node;
|
||
import javafx.scene.control.Alert;
|
||
import javafx.scene.control.ButtonType;
|
||
import javafx.scene.control.Dialog;
|
||
import javafx.scene.control.DialogPane;
|
||
import javafx.scene.control.Label;
|
||
import javafx.scene.control.ListView;
|
||
import javafx.scene.control.ProgressBar;
|
||
import javafx.scene.control.TextArea;
|
||
import javafx.scene.control.TextField;
|
||
import javafx.scene.layout.GridPane;
|
||
import javafx.scene.layout.Priority;
|
||
import javafx.scene.layout.VBox;
|
||
import javafx.stage.Screen;
|
||
import javafx.util.StringConverter;
|
||
|
||
public class UITools {
|
||
private static final Logger logger = LoggerFactory.getLogger(UITools.class);
|
||
|
||
public static void showExceptionAndWait(String title, Throwable e) {
|
||
String stackTrace = printStackTrace(e);
|
||
Label label = new Label("异常堆栈信息如下:");
|
||
TextArea textArea = new TextArea(stackTrace);
|
||
textArea.setEditable(false);
|
||
textArea.setWrapText(true);
|
||
|
||
GridPane.setVgrow(textArea, Priority.ALWAYS);
|
||
GridPane.setHgrow(textArea, Priority.ALWAYS);
|
||
|
||
GridPane expContent = new GridPane();
|
||
expContent.setMaxWidth(Double.MAX_VALUE);
|
||
expContent.add(label, 0, 0);
|
||
expContent.add(textArea, 0, 1);
|
||
|
||
String message = e.getMessage();
|
||
showAndWait(Alert.AlertType.ERROR, "程序异常", title, message, expContent);
|
||
}
|
||
|
||
/**
|
||
* 显示 Alert 窗口
|
||
*
|
||
* @param alertType alert 类型
|
||
* @param title 标题
|
||
* @param header 头
|
||
* @param message 消息内容
|
||
* @param content 扩张节点
|
||
*/
|
||
public static CompletableFuture<ButtonType> showAndWait(Alert.AlertType alertType, String title, String header,
|
||
String message, Node content) {
|
||
CompletableFuture<ButtonType> future = new CompletableFuture<>();
|
||
Platform.runLater(() -> {
|
||
Alert alert = new Alert(alertType);
|
||
if (content != null) {
|
||
alert.getDialogPane().setExpandableContent(content);
|
||
alert.getDialogPane().setExpanded(true);
|
||
}
|
||
alert.setTitle(title);
|
||
if (header != null)
|
||
alert.setHeaderText(header);
|
||
if (message != null)
|
||
alert.setContentText(message);
|
||
|
||
Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();
|
||
alert.getDialogPane().setMaxWidth(visualBounds.getWidth() * 0.9);
|
||
alert.showAndWait();
|
||
future.complete(alert.getResult());
|
||
});
|
||
return future;
|
||
}
|
||
|
||
public static CompletableFuture<ButtonType> showConfirmation(String title, String message) {
|
||
return showAndWait(Alert.AlertType.CONFIRMATION, "请选择", title, message, null);
|
||
}
|
||
|
||
public static <T> Dialog<T> createDialog() {
|
||
Dialog<T> dialog = new Dialog<>();
|
||
VBox box = new VBox();
|
||
Label headerLabel = new Label();
|
||
headerLabel.setId("header");
|
||
|
||
VBox.setMargin(headerLabel, new Insets(5, 0, 8, 0));
|
||
|
||
ListView<T> listView = new ListView<>();
|
||
listView.setMinHeight(50);
|
||
listView.setId("list-view");
|
||
ObservableList<T> listViewDataSet = FXCollections.observableArrayList();
|
||
listView.setItems(listViewDataSet);
|
||
VBox.setVgrow(listView, Priority.ALWAYS);
|
||
|
||
box.getChildren().setAll(headerLabel, listView);
|
||
|
||
dialog.getDialogPane().setContent(box);
|
||
dialog.getDialogPane().getButtonTypes().add(ButtonType.OK);
|
||
dialog.setResizable(true);
|
||
dialog.setWidth(600);
|
||
dialog.setHeight(500);
|
||
|
||
return dialog;
|
||
}
|
||
|
||
/**
|
||
* 显示一个对话框, 并等待用户关闭对话框
|
||
* <p>
|
||
* 对话框内嵌一个 ListView,可在 consumer 中向 ListView 添加内容
|
||
*
|
||
* @param title 对话框标题
|
||
* @param header 对话框头
|
||
* @param consumer 对话框内容生成器
|
||
*/
|
||
public static void showDialogAndWait(String title, String header, Consumer<ObservableList<String>> consumer) {
|
||
Dialog<String> dialog = createDialog();
|
||
dialog.setTitle(title);
|
||
|
||
DialogPane dialogPane = dialog.getDialogPane();
|
||
Node content = dialogPane.getContent();
|
||
|
||
Label headerLabel = (Label) content.lookup("#header");
|
||
ListView<String> listView = (ListView<String>) content.lookup("#list-view");
|
||
headerLabel.setText(header);
|
||
CompletableFuture.runAsync(() -> {
|
||
consumer.accept(listView.getItems());
|
||
}).exceptionally(e -> {
|
||
Platform.runLater(() -> {
|
||
listView.getItems().add(e.getMessage());
|
||
});
|
||
if (logger.isErrorEnabled()) {
|
||
logger.error(e.getMessage(), e);
|
||
}
|
||
return null;
|
||
});
|
||
dialog.showAndWait();
|
||
}
|
||
|
||
/**
|
||
* 显示一个对话框, 并等待用户关闭对话框
|
||
*
|
||
* @param title 对话框标题
|
||
* @param task 任务
|
||
* @param init 初始化
|
||
*/
|
||
public static void showTaskDialogAndWait(String title, javafx.concurrent.Task<?> task,
|
||
Consumer<Consumer<Message>> init) {
|
||
Dialog<Message> dialog = createDialog();
|
||
dialog.getDialogPane().getScene().getStylesheets().add("/ui/dialog.css");
|
||
dialog.setTitle(title);
|
||
|
||
DialogPane dialogPane = dialog.getDialogPane();
|
||
VBox box = (VBox) dialogPane.getContent();
|
||
Label headerLabel = (Label) box.lookup("#header");
|
||
ListView<Message> listView = (ListView<Message>) box.lookup("#list-view");
|
||
listView.setCellFactory(param -> new Tasker.MessageListCell());
|
||
|
||
headerLabel.textProperty().bind(task.titleProperty());
|
||
|
||
java.util.function.Predicate<Message> consumer = message -> {
|
||
if (Platform.isFxApplicationThread()) {
|
||
listView.getItems().add(message);
|
||
} else {
|
||
Platform.runLater(() -> listView.getItems().add(message));
|
||
}
|
||
return false;
|
||
};
|
||
if (task instanceof Tasker<?> tasker) {
|
||
tasker.setMessageHandler(consumer);
|
||
} else {
|
||
task.messageProperty().addListener((observable, oldValue, newValue) -> {
|
||
consumer.test(Message.info(newValue));
|
||
});
|
||
}
|
||
|
||
// 加一个进度条
|
||
ProgressBar progressBar = new ProgressBar(0);
|
||
progressBar.setPrefHeight(12);
|
||
progressBar.setPrefWidth(200);
|
||
progressBar.prefWidthProperty().bind(box.widthProperty().subtract(10));
|
||
progressBar.setMinHeight(12);
|
||
progressBar.setVisible(true);
|
||
VBox.setMargin(progressBar, new Insets(6, 0, 6, 0));
|
||
|
||
Platform.runLater(() -> {
|
||
box.getChildren().add(progressBar);
|
||
});
|
||
|
||
// progressBar.disabledProperty().bind(task.runningProperty());
|
||
progressBar.visibleProperty().bind(task.runningProperty());
|
||
progressBar.progressProperty().bind(task.progressProperty());
|
||
task.progressProperty().addListener((observable, oldValue, newValue) -> {
|
||
System.out.println("progress = " + newValue);
|
||
});
|
||
|
||
if (task instanceof Tasker<?> tasker) {
|
||
// 提交任务
|
||
new Thread(tasker).start();
|
||
// try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
|
||
// executor.submit(tasker);
|
||
// System.out.println("executor = " + executor);
|
||
// }
|
||
}
|
||
if (init != null) {
|
||
init.accept(consumer::test);
|
||
}
|
||
dialog.showAndWait();
|
||
task.cancel();
|
||
}
|
||
|
||
private static String printStackTrace(Throwable e) {
|
||
StringWriter sw = new StringWriter();
|
||
PrintWriter pw = new PrintWriter(sw);
|
||
e.printStackTrace(pw);
|
||
return sw.toString();
|
||
}
|
||
|
||
public static void showAlertAndWait(String message) {
|
||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||
alert.setContentText(message);
|
||
alert.showAndWait();
|
||
}
|
||
|
||
public static <T> AutoCompletionBinding<T> autoCompletion(
|
||
TextField textField,
|
||
SimpleObjectProperty<T> property,
|
||
EntityStringConverter<T> converter) {
|
||
return autoCompletion(textField, property, converter::suggest, converter);
|
||
}
|
||
|
||
public static <T extends IdentityEntity, TV extends IdentityViewModel<T>> AutoCompletionBinding<T> autoCompletion(
|
||
TextField textField, ObjectProperty<Integer> idProperty, QueryService<T, TV> queryService) {
|
||
StringConverter<T> converter = queryService.getStringConverter();
|
||
|
||
return autoCompletion(textField, idProperty, queryService, converter, queryService::search);
|
||
}
|
||
|
||
public static <T extends IdentityEntity, TV extends IdentityViewModel<T>> AutoCompletionBinding<T> autoCompletion(
|
||
TextField textField, ObjectProperty<Integer> idProperty, QueryService<T, TV> service, StringConverter<T> converter, Function<String, List<T>> searcher) {
|
||
Integer id = idProperty.get();
|
||
if (id != null) {
|
||
T entity = service.findById(id);
|
||
// 先赋值
|
||
textField.textProperty().set(converter.toString(entity));
|
||
}
|
||
|
||
// 监听 property 变化
|
||
idProperty.addListener((observable, oldValue, newValue) -> {
|
||
if (newValue == null) {
|
||
textField.textProperty().set(null);
|
||
return;
|
||
}
|
||
T newEntity = service.findById(newValue);
|
||
textField.textProperty().set(converter.toString(newEntity));
|
||
});
|
||
|
||
// 关联一个 自动完成
|
||
AutoCompletionBinding<T> completionBinding = TextFields.bindAutoCompletion(textField, p -> {
|
||
if (p.isCancelled()) {
|
||
return null;
|
||
}
|
||
try {
|
||
return searcher.apply(p.getUserText());
|
||
} catch (Exception e) {
|
||
textField.setText(e.getMessage());
|
||
throw new RuntimeException(e);
|
||
}
|
||
}, converter);
|
||
|
||
// 设置自动补全选中值到 property
|
||
completionBinding.setOnAutoCompleted(event -> {
|
||
T completion = event.getCompletion();
|
||
idProperty.set(completion.getId());
|
||
});
|
||
|
||
// fixed 当输入框丢失焦点时,输入框内容为空时,设置property 为null
|
||
textField.focusedProperty().addListener((observable, oldValue, newValue) -> {
|
||
if (!newValue) {
|
||
if (!StringUtils.hasText(textField.getText())) {
|
||
idProperty.set(null);
|
||
}
|
||
}
|
||
});
|
||
|
||
return completionBinding;
|
||
}
|
||
|
||
/**
|
||
* 给 TextField 增加一个 自动补全功能
|
||
*
|
||
* @param textField 要补全的文本框
|
||
* @param property 绑定的属性
|
||
* @param func 获取补全数据的方法
|
||
* @param converter 对象转换器
|
||
* @param <T> 对象类型
|
||
* @return AutoCompletionBinding
|
||
*/
|
||
public static <T> AutoCompletionBinding<T> autoCompletion(
|
||
TextField textField,
|
||
SimpleObjectProperty<T> property,
|
||
Function<AutoCompletionBinding.ISuggestionRequest, List<T>> func,
|
||
StringConverter<T> converter) {
|
||
// 先赋值
|
||
textField.textProperty().set(converter.toString(property.get()));
|
||
// 监听 property 变化
|
||
property.addListener((observable, oldValue, newValue) -> {
|
||
textField.textProperty().set(converter.toString(newValue));
|
||
});
|
||
|
||
// 关联一个 自动完成
|
||
AutoCompletionBinding<T> completionBinding = TextFields.bindAutoCompletion(textField, p -> {
|
||
if (p.isCancelled()) {
|
||
return null;
|
||
}
|
||
try {
|
||
return func.apply(p);
|
||
} catch (Exception e) {
|
||
textField.setText(e.getMessage());
|
||
throw new RuntimeException(e);
|
||
}
|
||
}, converter);
|
||
|
||
// 设置自动补全选中值到 property
|
||
completionBinding.setOnAutoCompleted(event -> {
|
||
property.set(event.getCompletion());
|
||
});
|
||
|
||
// fixed 当输入框丢失焦点时,输入框内容为空时,设置property 为null
|
||
textField.focusedProperty().addListener((observable, oldValue, newValue) -> {
|
||
if (!newValue) {
|
||
if (!StringUtils.hasText(textField.getText())) {
|
||
property.set(null);
|
||
}
|
||
}
|
||
});
|
||
|
||
return completionBinding;
|
||
}
|
||
|
||
/**
|
||
* 确认对话框
|
||
*
|
||
* @param title
|
||
* @param text
|
||
* @return
|
||
*/
|
||
public static boolean showConfirmDialog(String title, String text) {
|
||
// TODO
|
||
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
|
||
alert.setTitle(title);
|
||
alert.setHeaderText(text);
|
||
if (alert.showAndWait().get() == ButtonType.OK) {
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
}
|