Files
contract-manager/client/src/main/java/com/ecep/contract/util/UITools.java
songqq 72edb07798 feat: 添加合同全量同步任务和合格供方名录路径生成功能
refactor: 重构查询服务使用ParamConstant替换ServiceConstant
style: 清理无用代码和注释
fix: 修复CompanyCustomerEvaluationFormFileService查询方法
docs: 更新CloudYuVo和CompanyBankAccountVo字段注释
2025-12-13 11:11:37 +08:00

385 lines
14 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
}
}