稳定可用版本

This commit is contained in:
张家训
2026-06-19 15:21:05 +08:00
parent 15157acd4e
commit 4c844159f3
26 changed files with 4050 additions and 252 deletions
@@ -1,26 +1,43 @@
package com.example.workutils;
import com.example.workutils.system.config.*;
import com.example.workutils.system.plugin.Plugin;
import com.example.workutils.system.plugin.PluginDescriptor;
import com.example.workutils.system.plugin.PluginManager;
import com.example.workutils.system.plugin.VisualPluginFactory;
import com.example.workutils.system.support.JfxBootstrap;
import com.example.workutils.system.support.WinConsole;
import com.example.workutils.system.ui.MainWindow;
import javafx.application.Platform;
import javafx.scene.Parent;
import javafx.scene.control.Alert;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import java.util.Arrays;
import java.util.Optional;
import java.util.Scanner;
import java.util.*;
import java.util.concurrent.CountDownLatch;
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@Slf4j
public class WorkUtilsApplication implements CommandLineRunner {
private final PluginManager pluginManager;
private final WorkUtilsProperties properties;
private final SettingsService settingsService;
public WorkUtilsApplication(PluginManager pluginManager) {
/** 主窗口引用,供内嵌插件面板使用 */
private MainWindow mainWindow;
/** 系统设置窗口缓存(内嵌模式复用) */
private SettingsWindow settingsWindow;
public WorkUtilsApplication(PluginManager pluginManager, WorkUtilsProperties properties,
SettingsService settingsService) {
this.pluginManager = pluginManager;
this.properties = properties;
this.settingsService = settingsService;
}
public static void main(String[] args) {
@@ -31,29 +48,188 @@ public class WorkUtilsApplication implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
if (args.length == 0) {
showMainWindow();
} else if ("--console".equals(args[0])) {
showInteractiveMenu();
} else {
processCommand(args);
}
}
// ==================== 主窗口入口 ====================
private void showMainWindow() throws InterruptedException {
JfxBootstrap.await();
if (JfxBootstrap.isFailed()) {
System.err.println("JavaFX 初始化失败,回退到控制台模式");
showInteractiveMenu();
return;
}
List<MainWindow.NavItem> navItems = buildNavItems();
CountDownLatch windowClosed = new CountDownLatch(1);
Platform.runLater(() -> {
try {
mainWindow = new MainWindow(navItems, properties);
mainWindow.showAndWait();
} catch (Exception e) {
log.error("主窗口创建失败", e);
System.err.println("主窗口创建失败: " + e.getMessage());
} finally {
windowClosed.countDown();
}
});
windowClosed.await();
log.info("主窗口已关闭");
}
// ==================== 导航项构建 ====================
private List<MainWindow.NavItem> buildNavItems() {
List<MainWindow.NavItem> items = new ArrayList<>();
for (Plugin plugin : pluginManager.getEnabledPlugins()) {
PluginDescriptor desc = plugin.getDescriptor();
VisualPluginFactory factory = plugin.getVisualFactory();
String pluginId = desc.getId();
items.add(new MainWindow.NavItem(
desc.getIcon(),
desc.getName(),
desc.getDescription(),
factory != null ? () -> openPluginEmbedded(pluginId, plugin, factory) : null,
pluginId
));
}
// 系统设置 — 内嵌为 Tab
items.add(new MainWindow.NavItem(
"⚙️", "系统设置",
"配置系统路径、数据库连接、项目路径、多数据源、字体大小等",
this::openSettingsEmbedded,
"__settings__"
));
return items;
}
// ==================== 内嵌优先打开 ====================
/**
* 优先尝试内嵌模式;若插件不支持嵌入,则退回到独立窗口。
* <p>
* 在 JFX 线程上调用(来自导航按钮事件)。
*/
private void openPluginEmbedded(String pluginId, Plugin plugin, VisualPluginFactory factory) {
// 配置检查
String configError = plugin.checkConfig();
if (configError != null) {
showConfigAlert(plugin.getName(), configError);
return;
}
// 尝试获取内嵌面板
Parent embeddedPane = factory.getEmbeddedPane();
if (embeddedPane != null && mainWindow != null) {
PluginDescriptor desc = plugin.getDescriptor();
mainWindow.embedPlugin(pluginId, desc.getIcon(), desc.getName(), embeddedPane);
return;
}
// 回退:独立窗口模式
openPluginStandalone(plugin, factory);
}
// ==================== 独立窗口模式(弹出或回退) ====================
/**
* 以独立窗口形式打开插件。用于:
* <ul>
* <li>插件不支持内嵌时的回退</li>
* <li>用户点击「弹出窗口」按钮</li>
* </ul>
*/
private void openPluginStandalone(Plugin plugin, VisualPluginFactory factory) {
new Thread(() -> {
try {
JfxBootstrap.await();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
return;
}
CountDownLatch windowClosed = new CountDownLatch(1);
Platform.runLater(() -> {
try {
factory.open(windowClosed::countDown);
} catch (Exception e) {
log.error("独立窗口创建失败: {}", plugin.getName(), e);
windowClosed.countDown();
}
});
try {
windowClosed.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, plugin.getId() + "-Window").start();
}
// ==================== 配置提示 ====================
private void showConfigAlert(String pluginName, String message) {
Alert alert = new Alert(Alert.AlertType.WARNING);
alert.setTitle("配置检查");
alert.setHeaderText(pluginName + " 配置不完整");
alert.setContentText(message);
alert.showAndWait();
}
// ==================== 系统设置 ====================
private void openSettingsEmbedded() {
if (settingsWindow == null) {
settingsWindow = settingsService.createSettingsWindow(null);
}
if (mainWindow != null) {
Parent pane = settingsWindow.getContentPane();
mainWindow.embedPlugin("__settings__", "⚙️", "系统设置", pane);
}
}
private void openSettingsStandalone() {
if (settingsWindow == null) {
settingsWindow = settingsService.createSettingsWindow(null);
}
javafx.stage.Stage s = settingsWindow.getStage();
if (s != null) {
s.show();
}
}
/** @deprecated 保留兼容旧路径 */
private void openSettingsWindow() {
new Thread(() -> settingsService.showSettings(null, null),
"SettingsWindow-Thread").start();
}
// ==================== 控制台模式(降级) ====================
private void showInteractiveMenu() {
Scanner scanner = new Scanner(System.in);
pluginManager.runMainMenu(scanner);
}
/**
* 命令行模式:通过 PluginManager 按命令名分发到对应 Tool
* 启动类不依赖任何具体业务 Service
*/
private void processCommand(String... args) throws Exception {
String command = args[0].toLowerCase();
Optional<Plugin> toolOpt = pluginManager.getPluginByCommand(command);
if (toolOpt.isPresent()) {
Plugin plugin = toolOpt.get();
try {
String[] toolArgs = args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) : new String[0];
String[] toolArgs = args.length > 1
? Arrays.copyOfRange(args, 1, args.length) : new String[0];
plugin.executeCommand(toolArgs);
} catch (Exception e) {
log.error("命令执行失败: {} [{}]", plugin.getName(), plugin.getId(), e);