From 400a2bffc225a85aa6a9c874079a8582ccffd042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=AE=B6=E8=AE=AD?= Date: Fri, 19 Jun 2026 16:47:39 +0800 Subject: [PATCH] =?UTF-8?q?UI=E6=95=B4=E4=BD=93=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 60 +- .../workutils/WorkUtilsApplication.java | 12 + .../workutils/biz/apidoc/ApiDocWindow.java | 334 +++++++-- .../workutils/biz/diff/DiffWindow.java | 2 +- .../workutils/biz/git/GitSyncWindow.java | 26 +- .../mybatisexport/MyBatisExportWindow.java | 26 +- .../MyBatisGeneratorWindow.java | 85 +-- .../generator/MyBatisGeneratorService.java | 5 + .../workutils/biz/scanner/ScannerWindow.java | 25 +- .../system/config/ExternalConfigManager.java | 41 +- .../system/config/SettingsWindow.java | 658 +++++++++++------- .../system/config/WorkUtilsProperties.java | 73 +- .../workutils/system/ui/MainWindow.java | 136 ++-- .../workutils/system/ui/StyledButton.java | 41 +- .../example/workutils/system/ui/Theme.java | 234 ++++--- src/main/resources/css/dark-theme.css | 287 ++++++++ src/main/resources/css/light-theme.css | 287 ++++++++ 17 files changed, 1660 insertions(+), 672 deletions(-) create mode 100644 src/main/resources/css/dark-theme.css create mode 100644 src/main/resources/css/light-theme.css diff --git a/pom.xml b/pom.xml index 53bd980..0d5b840 100644 --- a/pom.xml +++ b/pom.xml @@ -14,20 +14,31 @@ workutils workutils - 8 - 8 - 8 + 17 + 17 + 17 + 17 UTF-8 UTF-8 -Dfile.encoding=UTF-8 + + 17.0.14 + + 2.0.1 - + local-lib-repo file://${project.basedir}/lib + + + sonatype-snapshots + https://s01.oss.sonatype.org/content/repositories/snapshots/ + true + @@ -130,12 +141,43 @@ - - + - com.oracle.javafx - jfxrt - 8.0 + org.openjfx + javafx-controls + ${javafx.version} + + + org.openjfx + javafx-web + ${javafx.version} + + + org.openjfx + javafx-fxml + ${javafx.version} + + + org.openjfx + javafx-base + ${javafx.version} + + + org.openjfx + javafx-graphics + ${javafx.version} + + + org.openjfx + javafx-swing + ${javafx.version} + + + + + io.github.mkpaz + atlantafx-base + ${atlantafx.version} diff --git a/src/main/java/com/example/workutils/WorkUtilsApplication.java b/src/main/java/com/example/workutils/WorkUtilsApplication.java index df0580b..5e330e9 100644 --- a/src/main/java/com/example/workutils/WorkUtilsApplication.java +++ b/src/main/java/com/example/workutils/WorkUtilsApplication.java @@ -8,6 +8,7 @@ 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 com.example.workutils.system.ui.Theme; import javafx.application.Platform; import javafx.scene.Parent; import javafx.scene.control.Alert; @@ -24,6 +25,13 @@ import java.util.concurrent.CountDownLatch; @Slf4j public class WorkUtilsApplication implements CommandLineRunner { + static { + // 消除 JavaFX classpath 运行时的 WARNING: Unsupported JavaFX configuration + java.util.logging.Logger rootLogger = java.util.logging.Logger.getLogger(""); + rootLogger.setFilter(record -> + !record.getMessage().contains("Unsupported JavaFX configuration")); + } + private final PluginManager pluginManager; private final WorkUtilsProperties properties; private final SettingsService settingsService; @@ -66,6 +74,10 @@ public class WorkUtilsApplication implements CommandLineRunner { return; } + // 始终使用亮色主题 + Theme.Mode mode = Theme.Mode.LIGHT; + Platform.runLater(() -> Theme.applyTheme(mode)); + List navItems = buildNavItems(); CountDownLatch windowClosed = new CountDownLatch(1); diff --git a/src/main/java/com/example/workutils/biz/apidoc/ApiDocWindow.java b/src/main/java/com/example/workutils/biz/apidoc/ApiDocWindow.java index 74fee4b..1d51c01 100644 --- a/src/main/java/com/example/workutils/biz/apidoc/ApiDocWindow.java +++ b/src/main/java/com/example/workutils/biz/apidoc/ApiDocWindow.java @@ -5,24 +5,30 @@ import com.example.workutils.biz.apidoc.model.ApiDocModel; import com.example.workutils.biz.apidoc.parser.JavaSourceParser; import com.example.workutils.system.config.WorkUtilsProperties; import com.example.workutils.system.support.FileOperationHelper; +import com.example.workutils.system.support.HistoryPersistence; import com.example.workutils.system.ui.StyledButton; import com.example.workutils.system.ui.Theme; import javafx.application.Platform; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.*; +import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.layout.*; import javafx.scene.Parent; import javafx.stage.DirectoryChooser; import javafx.stage.FileChooser; import javafx.stage.Stage; +import java.awt.Desktop; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.text.SimpleDateFormat; import java.util.*; import java.util.concurrent.CountDownLatch; import java.util.stream.Stream; @@ -64,14 +70,24 @@ public class ApiDocWindow { private Label progressLabel; private TextArea logArea; + // 历史记录 + private TableView historyTable; + private final ObservableList historyRows = FXCollections.observableArrayList(); + private int historyCounter = 0; + private final HistoryPersistence historyPersistence; + private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + private static final String DELIMITER = "|"; + public ApiDocWindow(FileOperationHelper fileOperationHelper, WorkUtilsProperties properties, CountDownLatch closeLatch) { this.fileOperationHelper = fileOperationHelper; this.properties = properties; this.closeLatch = closeLatch; + this.historyPersistence = new HistoryPersistence("apidoc_history.txt"); this.stage = new Stage(); buildUI(); loadProjects(); + loadHistory(); } public void show() { @@ -90,27 +106,33 @@ public class ApiDocWindow { private void buildUI() { stage.setTitle("API 接口文档导出"); - stage.setWidth(800); - stage.setHeight(600); - stage.setMinWidth(650); - stage.setMinHeight(450); + stage.setWidth(900); + stage.setHeight(700); + stage.setMinWidth(700); + stage.setMinHeight(550); stage.setOnCloseRequest(e -> closeLatch.countDown()); - VBox root = new VBox(Theme.S3); + ScrollPane scrollRoot = new ScrollPane(); + scrollRoot.setFitToWidth(true); + scrollRoot.getStyleClass().add("edge-to-edge"); + + VBox root = new VBox(Theme.S4); root.setPadding(new Insets(Theme.S4)); root.setStyle("-fx-background-color: " + Theme.toWeb(Theme.BG_BODY) + ";"); - // === 模式选择 === - VBox modeBox = new VBox(Theme.S2); - modeBox.setPadding(new Insets(Theme.S4)); - modeBox.setStyle(Theme.STYLE_CARD); + // ===================== 配置卡片 ===================== + VBox configCard = new VBox(Theme.S3); + configCard.setPadding(new Insets(Theme.S4)); + configCard.getStyleClass().add(Theme.CSS_CARD); + // --- 导出模式 --- Label modeLabel = new Label("导出模式"); modeLabel.setFont(Theme.heading(fsSection() + 2)); modeLabel.setTextFill(Theme.TEXT_PRIMARY); modeGroup = new ToggleGroup(); HBox modeRow = new HBox(Theme.S5); + modeRow.setAlignment(Pos.CENTER_LEFT); rbCustomPath = new RadioButton("自定义路径"); rbFqn = new RadioButton("全限定名"); rbBatch = new RadioButton("批量导出"); @@ -123,17 +145,15 @@ public class ApiDocWindow { rbBatch.setFont(Theme.base(fsBody())); modeRow.getChildren().addAll(rbCustomPath, rbFqn, rbBatch); - // 输入区域(StackPane 切换) + // 输入区域 StackPane inputStack = new StackPane(); - inputStack.setPrefHeight(100); + inputStack.setPadding(new Insets(Theme.S2, 0, 0, 0)); - // 自定义路径面板 VBox customPanel = new VBox(Theme.S2); pathField = new TextField(); pathField.setFont(Theme.base(fsBody())); - pathField.setStyle(Theme.STYLE_TEXT_FIELD); - pathField.setPrefWidth(500); - pathField.setPromptText("输入文件路径或目录路径,或点击右侧按钮选择"); + pathField.getStyleClass().add(Theme.CSS_TEXT_FIELD); + pathField.setPromptText("输入文件路径或目录路径"); HBox btnRow = new HBox(Theme.S2); browseFileBtn = StyledButton.small("选择文件", Theme.base(fsBody() - 1)); browseDirBtn = StyledButton.small("选择目录", Theme.base(fsBody() - 1)); @@ -142,36 +162,33 @@ public class ApiDocWindow { btnRow.getChildren().addAll(browseFileBtn, browseDirBtn); customPanel.getChildren().addAll(pathField, btnRow); - // 全限定名面板 VBox fqnPanel = new VBox(Theme.S2); fqnField = new TextField(); fqnField.setFont(Theme.base(fsBody())); - fqnField.setStyle(Theme.STYLE_TEXT_FIELD); - fqnField.setPrefWidth(500); + fqnField.getStyleClass().add(Theme.CSS_TEXT_FIELD); fqnField.setPromptText("例如: com.example.service.UserFacade"); fqnPanel.getChildren().addAll(new Label("全限定类名:"), fqnField); - // 批量面板 - VBox batchPanel = new VBox(Theme.S2); + VBox batchPanel = new VBox(Theme.S3); projectCmb = new ComboBox<>(); - projectCmb.setStyle(Theme.STYLE_COMBO + "-fx-font-family: 'Microsoft YaHei'; -fx-font-size: " + fsBody() + "px;"); - projectCmb.setPrefWidth(Math.max(400, fsBody() * 28)); // 动态宽度,保证长路径完整显示 + projectCmb.getStyleClass().add(Theme.CSS_COMBO); + projectCmb.setStyle("-fx-font-family: 'Microsoft YaHei'; -fx-font-size: " + fsBody() + "px;"); + projectCmb.setMaxWidth(Double.MAX_VALUE); projectCmb.setPromptText("选择项目"); allProjectsCb = new CheckBox("全部项目"); allProjectsCb.setFont(Theme.base(fsBody() - 1)); batchPanel.getChildren().addAll(projectCmb, allProjectsCb); inputStack.getChildren().addAll(customPanel, fqnPanel, batchPanel); - - // 切换可见性 customPanel.visibleProperty().bind(rbCustomPath.selectedProperty()); fqnPanel.visibleProperty().bind(rbFqn.selectedProperty()); batchPanel.visibleProperty().bind(rbBatch.selectedProperty()); - modeBox.getChildren().addAll(modeLabel, modeRow, inputStack); + // --- 分割线 --- + Separator sep1 = new Separator(); - // === 导出格式 === - HBox fmtRow = new HBox(Theme.S5); + // --- 导出格式 --- + HBox fmtRow = new HBox(Theme.S4); fmtRow.setAlignment(Pos.CENTER_LEFT); Label fmtLabel = new Label("导出格式:"); fmtLabel.setFont(Theme.base(fsBody())); @@ -189,14 +206,17 @@ public class ApiDocWindow { rbBoth.setFont(Theme.base(fsBody() - 1)); fmtRow.getChildren().addAll(fmtLabel, rbHtml, rbMd, rbBoth); - // === 操作按钮 === + // --- 分割线 --- + Separator sep2 = new Separator(); + + // --- 操作按钮 --- HBox actionRow = new HBox(Theme.S3); actionRow.setAlignment(Pos.CENTER_LEFT); exportBtn = StyledButton.primary("导出文档", Theme.bold(fsBtn())); exportBtn.setOnAction(e -> startExport()); progressBar = new ProgressBar(0); - progressBar.setPrefWidth(300); + progressBar.setPrefWidth(280); progressBar.setPrefHeight(16); progressBar.setVisible(false); @@ -206,15 +226,59 @@ public class ApiDocWindow { actionRow.getChildren().addAll(exportBtn, progressBar, progressLabel); - // === 日志 === + configCard.getChildren().addAll(modeLabel, modeRow, inputStack, sep1, fmtRow, sep2, actionRow); + + // ===================== 历史记录 ===================== + Label histTitle = new Label("导出历史"); + histTitle.setFont(Theme.heading(fsSection() + 1)); + histTitle.setTextFill(Theme.TEXT_PRIMARY); + + historyTable = new TableView<>(); + historyTable.setItems(historyRows); + historyTable.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY); + historyTable.setPlaceholder(new Label("暂无导出记录")); + historyTable.setPrefHeight(180); + historyTable.setStyle("-fx-background-color: white;" + + "-fx-border-color: " + Theme.toWeb(Theme.BORDER) + ";" + + "-fx-border-width: 1; -fx-border-radius: " + Theme.RADIUS_SM + ";"); + + TableColumn colIdx = new TableColumn<>("序号"); + colIdx.setCellValueFactory(new PropertyValueFactory<>("index")); + colIdx.setPrefWidth(50); + + TableColumn colMode = new TableColumn<>("导出模式"); + colMode.setCellValueFactory(new PropertyValueFactory<>("mode")); + colMode.setPrefWidth(90); + + TableColumn colTarget = new TableColumn<>("目标"); + colTarget.setCellValueFactory(new PropertyValueFactory<>("target")); + + TableColumn colFile = new TableColumn<>("导出文件"); + colFile.setCellValueFactory(new PropertyValueFactory<>("fileDisplay")); + + TableColumn colTime = new TableColumn<>("时间"); + colTime.setCellValueFactory(new PropertyValueFactory<>("timestamp")); + colTime.setPrefWidth(140); + + TableColumn colAction = new TableColumn<>("操作"); + colAction.setCellValueFactory(new PropertyValueFactory<>("actionLabel")); + colAction.setCellFactory(col -> buildActionCell()); + colAction.setPrefWidth(170); + colAction.setResizable(false); + + historyTable.getColumns().addAll(colAction, colIdx, colMode, colTarget, colFile, colTime); + + // ===================== 日志 ===================== logArea = new TextArea(); logArea.setEditable(false); logArea.setFont(Theme.mono(properties.getFontMono())); - logArea.setStyle(Theme.STYLE_LOG_AREA); + logArea.getStyleClass().add(Theme.CSS_LOG_AREA); + logArea.setPrefRowCount(5); VBox.setVgrow(logArea, Priority.ALWAYS); - root.getChildren().addAll(modeBox, fmtRow, actionRow, logArea); - stage.setScene(new Scene(root)); + root.getChildren().addAll(configCard, histTitle, historyTable, logArea); + scrollRoot.setContent(root); + stage.setScene(new Scene(scrollRoot)); } private void browseFile() { @@ -309,7 +373,7 @@ public class ApiDocWindow { doc.projectName = projName; doc.projectPath = inputPath; doc.classes = Collections.singletonList(ci); - doExport(projName, Collections.singletonList(doc)); + doExport(projName, Collections.singletonList(doc), "单文件", inputPath); } // ==================== 目录导出 ==================== @@ -337,7 +401,7 @@ public class ApiDocWindow { doc.projectName = projName; doc.projectPath = inputPath; doc.classes = classes; - doExport(projName, Collections.singletonList(doc)); + doExport(projName, Collections.singletonList(doc), "目录", inputPath); } // ==================== 全限定名导出 ==================== @@ -472,12 +536,17 @@ public class ApiDocWindow { } log("总计: " + allDocs.size() + " 个模块, " + totalClasses + " 个类"); - doExport("批量导出", allDocs); + doExport("批量导出", allDocs, "批量", String.valueOf(selectedPaths.size()) + "个项目"); } // ==================== 导出输出 ==================== private void doExport(String name, List docs) throws Exception { + doExport(name, docs, "单文件", ""); + } + + private void doExport(String name, List docs, + String mode, String target) throws Exception { ApiDocGenerator gen = new ApiDocGenerator(properties); ApiDocModel.ExportSelection sel = new ApiDocModel.ExportSelection(); sel.scope = ApiDocModel.ExportScope.CUSTOM; @@ -486,27 +555,42 @@ public class ApiDocWindow { String fmt = getFmt(); if ("1".equals(fmt)) { Path p = gen.generateHtml(sel, docs); + String filePath = p.toAbsolutePath().toString(); Platform.runLater(() -> { - log("HTML 已导出: " + p.toAbsolutePath()); + log("HTML 已导出: " + filePath); + addHistory(mode, target, filePath); enableBtn(); }); } else if ("2".equals(fmt)) { Path p = gen.generateMarkdown(sel, docs); + String filePath = p.toAbsolutePath().toString(); Platform.runLater(() -> { - log("Markdown 已导出: " + p.toAbsolutePath()); + log("Markdown 已导出: " + filePath); + addHistory(mode, target, filePath); enableBtn(); }); } else { Path html = gen.generateHtml(sel, docs); Path md = gen.generateMarkdown(sel, docs); + String htmlPath = html.toAbsolutePath().toString(); + String mdPath = md.toAbsolutePath().toString(); Platform.runLater(() -> { - log("HTML: " + html.toAbsolutePath()); - log("Markdown: " + md.toAbsolutePath()); + log("HTML: " + htmlPath); + log("Markdown: " + mdPath); + addHistory(mode, target, htmlPath); enableBtn(); }); } } + private void addHistory(String mode, String target, String filePath) { + int hc = ++historyCounter; + String timestamp = SDF.format(new Date()); + historyRows.add(0, new HistoryRow( + String.valueOf(hc), mode, target, filePath, timestamp, "打开")); + saveHistory(); + } + private void log(String msg) { Platform.runLater(() -> logArea.appendText(msg + "\n")); } @@ -518,6 +602,176 @@ public class ApiDocWindow { }); } + // ==================== 历史持久化 ==================== + + private void loadHistory() { + List lines = historyPersistence.load(); + int hc = 0; + for (String line : lines) { + String[] parts = line.split("\\" + DELIMITER, 5); + if (parts.length == 5) { + hc++; + historyRows.add(new HistoryRow( + String.valueOf(hc), parts[0], parts[1], parts[2], parts[3], parts[4])); + } + } + historyCounter = hc; + } + + private void saveHistory() { + List lines = new ArrayList<>(); + for (HistoryRow row : historyRows) { + lines.add(row.getMode() + DELIMITER + + row.getTarget() + DELIMITER + + row.getFileName() + DELIMITER + + row.getTimestamp() + DELIMITER + + row.getActionLabel()); + } + historyPersistence.save(lines); + } + + // ==================== 操作列 ==================== + + private TableCell buildActionCell() { + TableCell cell = new TableCell() { + private final Button openBtn = new Button("打开"); + private final Button dirBtn = new Button("目录"); + private final HBox btnBox = new HBox(4, openBtn, dirBtn); + { setAlignment(Pos.CENTER); btnBox.setAlignment(Pos.CENTER); } + private final ContextMenu menu = new ContextMenu(); + private String currentFileName; + + { + openBtn.setFont(Theme.base(fsBody() - 2)); + openBtn.setStyle("-fx-background-color: " + Theme.toWeb(Theme.PRIMARY) + ";" + + "-fx-text-fill: white; -fx-cursor: hand;" + + "-fx-background-radius: " + Theme.RADIUS_SM + "; -fx-padding: 3 8;"); + + dirBtn.setFont(Theme.base(fsBody() - 2)); + dirBtn.setStyle("-fx-background-color: " + Theme.toWeb(Theme.BORDER) + ";" + + "-fx-text-fill: " + Theme.toWeb(Theme.TEXT_PRIMARY) + "; -fx-cursor: hand;" + + "-fx-background-radius: " + Theme.RADIUS_SM + "; -fx-padding: 3 6;"); + dirBtn.setTooltip(new Tooltip("打开文件所在目录")); + + MenuItem defaultOpen = new MenuItem("系统默认程序打开"); + defaultOpen.setOnAction(e -> openFile(currentFileName)); + MenuItem openNotepad = new MenuItem("用记事本打开"); + openNotepad.setOnAction(e -> openWithNotepad(currentFileName)); + MenuItem openFolder = new MenuItem("打开所在文件夹"); + openFolder.setOnAction(e -> openFolder(currentFileName)); + MenuItem copyPath = new MenuItem("复制文件路径"); + copyPath.setOnAction(e -> copyFilePath(currentFileName)); + menu.getItems().addAll(defaultOpen, openNotepad, openFolder, copyPath); + + openBtn.setOnAction(e -> openFile(currentFileName)); + dirBtn.setOnAction(e -> openFolder(currentFileName)); + btnBox.setOnContextMenuRequested(e -> { + menu.show(btnBox, e.getScreenX(), e.getScreenY()); + }); + } + + @Override + protected void updateItem(String item, boolean empty) { + super.updateItem(item, empty); + if (empty) { + setGraphic(null); + currentFileName = null; + } else { + int idx = getIndex(); + if (idx >= 0 && getTableView() != null) { + HistoryRow row = getTableView().getItems().get(idx); + currentFileName = row != null ? row.getFileName() : null; + } + setGraphic(btnBox); + } + } + + private void copyFilePath(String path) { + if (path == null) return; + javafx.scene.input.Clipboard.getSystemClipboard() + .setContent(new javafx.scene.input.ClipboardContent() {{ + putString(path); + }}); + log("[INFO] 已复制路径: " + path); + } + }; + return cell; + } + + // ==================== 文件打开工具方法 ==================== + + private void openFile(String path) { + if (path == null) return; + try { + File f = new File(path); + if (!f.exists()) return; + String os = System.getProperty("os.name").toLowerCase(); + if (os.contains("win")) { + new ProcessBuilder("cmd", "/c", "start", "", path).start(); + } else { + Desktop.getDesktop().open(f); + } + } catch (Exception ignored) { + } + } + + private void openWithNotepad(String path) { + if (path == null) return; + try { + new ProcessBuilder("notepad.exe", path).start(); + } catch (Exception e) { + openFile(path); + } + } + + private void openFolder(String path) { + if (path == null) return; + try { + File f = new File(path).getParentFile(); + if (f == null || !f.exists()) return; + String os = System.getProperty("os.name").toLowerCase(); + if (os.contains("win")) { + new ProcessBuilder("explorer.exe", "/select,", path).start(); + } else { + Desktop.getDesktop().open(f); + } + } catch (Exception ignored) { + } + } + + // ==================== 数据行 ==================== + + public static class HistoryRow { + private final String index; + private final String mode; + private final String target; + private final String fileName; + private final String timestamp; + private final String actionLabel; + + HistoryRow(String index, String mode, String target, String fileName, + String timestamp, String actionLabel) { + this.index = index; + this.mode = mode; + this.target = target; + this.fileName = fileName; + this.timestamp = timestamp; + this.actionLabel = actionLabel; + } + + public String getIndex() { return index; } + public String getMode() { return mode; } + public String getTarget() { return target; } + public String getFileName() { return fileName; } + public String getFileDisplay() { + return fileName.contains("\\") || fileName.contains("/") + ? fileName.substring(Math.max(fileName.lastIndexOf('\\'), fileName.lastIndexOf('/')) + 1) + : fileName; + } + public String getTimestamp() { return timestamp; } + public String getActionLabel() { return actionLabel; } + } + // ==================== 工具方法(移植自 ApiDocPlugin) ==================== private String extractProjName(String path) { diff --git a/src/main/java/com/example/workutils/biz/diff/DiffWindow.java b/src/main/java/com/example/workutils/biz/diff/DiffWindow.java index a230257..b8b0218 100644 --- a/src/main/java/com/example/workutils/biz/diff/DiffWindow.java +++ b/src/main/java/com/example/workutils/biz/diff/DiffWindow.java @@ -208,7 +208,7 @@ public class DiffWindow { TextArea area = new TextArea(); area.setFont(monoFont()); - area.setStyle(Theme.STYLE_TEXT_AREA); + area.getStyleClass().add(Theme.CSS_TEXT_AREA); area.setWrapText(false); pasteBtn.setOnAction(e -> pasteFromClipboard(area)); diff --git a/src/main/java/com/example/workutils/biz/git/GitSyncWindow.java b/src/main/java/com/example/workutils/biz/git/GitSyncWindow.java index b97bb1f..1a615ff 100644 --- a/src/main/java/com/example/workutils/biz/git/GitSyncWindow.java +++ b/src/main/java/com/example/workutils/biz/git/GitSyncWindow.java @@ -132,7 +132,7 @@ public class GitSyncWindow { projectTable = new TableView<>(); projectTable.setItems(projectRows); - projectTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); + projectTable.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY); projectTable.setStyle("-fx-background-color: white;" + "-fx-border-color: " + Theme.toWeb(Theme.BORDER) + ";" + "-fx-border-width: 1; -fx-border-radius: " + Theme.RADIUS_SM + ";"); @@ -151,12 +151,6 @@ public class GitSyncWindow { projectTable.getColumns().addAll(colName, colPath, colStatus, colRemote); - // 列宽百分比自适应:项目名18% 路径38% Git状态12% 远程URL32% - colName.prefWidthProperty().bind(projectTable.widthProperty().subtract(18).multiply(0.18)); - colPath.prefWidthProperty().bind(projectTable.widthProperty().subtract(18).multiply(0.38)); - colStatus.prefWidthProperty().bind(projectTable.widthProperty().subtract(18).multiply(0.12)); - colRemote.prefWidthProperty().bind(projectTable.widthProperty().subtract(18).multiply(0.32)); - VBox.setVgrow(projectTable, Priority.ALWAYS); box.getChildren().addAll(title, toolbar, projectTable); tab.setContent(box); @@ -202,7 +196,7 @@ public class GitSyncWindow { branchLabel.setTextFill(Theme.TEXT_PRIMARY); branchField = new TextField("master"); branchField.setFont(Theme.base(14)); - branchField.setStyle(Theme.STYLE_TEXT_FIELD); + branchField.getStyleClass().add(Theme.CSS_TEXT_FIELD); branchField.setPrefWidth(200); syncBtn = StyledButton.primary("开始同步", Theme.bold(14)); @@ -232,7 +226,7 @@ public class GitSyncWindow { logArea = new TextArea(); logArea.setEditable(false); logArea.setFont(Theme.mono(13)); - logArea.setStyle(Theme.STYLE_LOG_AREA); + logArea.getStyleClass().add(Theme.CSS_LOG_AREA); VBox.setVgrow(logArea, Priority.ALWAYS); box.getChildren().addAll(branchRow, progressRow, logTitle, logArea); @@ -315,7 +309,7 @@ public class GitSyncWindow { // 表格 damagedTable = new TableView<>(); damagedTable.setItems(damagedRows); - damagedTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); + damagedTable.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY); damagedTable.setPrefHeight(350); damagedTable.setStyle("-fx-background-color: white;" + "-fx-border-color: " + Theme.toWeb(Theme.BORDER) + ";" @@ -335,9 +329,12 @@ public class GitSyncWindow { TableColumn colAction = new TableColumn<>("操作"); colAction.setCellValueFactory(new PropertyValueFactory<>("actionBtn")); + colAction.setPrefWidth(100); + colAction.setResizable(false); colAction.setCellFactory(col -> new TableCell() { private final Button btn = new Button("重新拉取"); { + setAlignment(Pos.CENTER); btn.setFont(Theme.base(12)); btn.setStyle("-fx-background-color: " + Theme.toWeb(Theme.WARNING) + ";" + "-fx-text-fill: white; -fx-cursor: hand;" @@ -356,14 +353,7 @@ public class GitSyncWindow { } }); - damagedTable.getColumns().addAll(colName2, colPath2, colError, colRemote2, colAction); - - // 列宽百分比自适应:项目名14% 路径26% 错误22% 远程28% 操作10% - colName2.prefWidthProperty().bind(damagedTable.widthProperty().subtract(18).multiply(0.14)); - colPath2.prefWidthProperty().bind(damagedTable.widthProperty().subtract(18).multiply(0.26)); - colError.prefWidthProperty().bind(damagedTable.widthProperty().subtract(18).multiply(0.22)); - colRemote2.prefWidthProperty().bind(damagedTable.widthProperty().subtract(18).multiply(0.28)); - colAction.prefWidthProperty().bind(damagedTable.widthProperty().subtract(18).multiply(0.10)); + damagedTable.getColumns().addAll(colAction, colName2, colPath2, colError, colRemote2); // 操作按钮 HBox actions = new HBox(Theme.S2); diff --git a/src/main/java/com/example/workutils/biz/mybatisexport/MyBatisExportWindow.java b/src/main/java/com/example/workutils/biz/mybatisexport/MyBatisExportWindow.java index fce2b42..d15effb 100644 --- a/src/main/java/com/example/workutils/biz/mybatisexport/MyBatisExportWindow.java +++ b/src/main/java/com/example/workutils/biz/mybatisexport/MyBatisExportWindow.java @@ -97,7 +97,7 @@ public class MyBatisExportWindow { HBox filterRow = new HBox(Theme.S3); filterRow.setAlignment(Pos.CENTER_LEFT); filterRow.setPadding(new Insets(Theme.S2, Theme.S3, Theme.S2, Theme.S3)); - filterRow.setStyle(Theme.STYLE_CARD); + filterRow.getStyleClass().add(Theme.CSS_CARD); Label modeLabel = new Label("过滤方式:"); modeLabel.setFont(Theme.base(fsBody())); @@ -105,12 +105,13 @@ public class MyBatisExportWindow { filterModeCmb = new ComboBox<>(FXCollections.observableArrayList( "全部SQL", "按表名", "按关键字", "按标签ID")); filterModeCmb.setValue("全部SQL"); - filterModeCmb.setStyle(Theme.STYLE_COMBO + "-fx-font-family: 'Microsoft YaHei'; -fx-font-size: " + (fsBody() - 1) + "px;"); + filterModeCmb.getStyleClass().add(Theme.CSS_COMBO); + filterModeCmb.setStyle("-fx-font-family: 'Microsoft YaHei'; -fx-font-size: " + (fsBody() - 1) + "px;"); filterModeCmb.setPrefWidth(Math.max(150, fsBody() * 11)); filterValueField = new TextField(); filterValueField.setFont(Theme.base(fsBody())); - filterValueField.setStyle(Theme.STYLE_TEXT_FIELD); + filterValueField.getStyleClass().add(Theme.CSS_TEXT_FIELD); filterValueField.setPrefWidth(250); filterValueField.setPromptText("输入过滤值"); filterValueField.setDisable(true); @@ -152,7 +153,7 @@ public class MyBatisExportWindow { // === 历史记录表格 === historyTable = new TableView<>(); historyTable.setItems(historyRows); - historyTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); + historyTable.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY); historyTable.setPlaceholder(new Label("暂无导出记录")); historyTable.setStyle("-fx-background-color: white;" + "-fx-border-color: " + Theme.toWeb(Theme.BORDER) + ";" @@ -177,16 +178,10 @@ public class MyBatisExportWindow { TableColumn colAction = new TableColumn<>("操作"); colAction.setCellValueFactory(new PropertyValueFactory<>("actionLabel")); colAction.setCellFactory(col -> buildActionCell()); + colAction.setPrefWidth(170); + colAction.setResizable(false); - historyTable.getColumns().addAll(colIndex, colQuery, colFile, colCount, colTime, colAction); - - // 列宽百分比自适应:序号5% 查询22% 文件30% 导出8% 时间17% 操作18% - colIndex.prefWidthProperty().bind(historyTable.widthProperty().subtract(18).multiply(0.05)); - colQuery.prefWidthProperty().bind(historyTable.widthProperty().subtract(18).multiply(0.22)); - colFile.prefWidthProperty().bind(historyTable.widthProperty().subtract(18).multiply(0.30)); - colCount.prefWidthProperty().bind(historyTable.widthProperty().subtract(18).multiply(0.08)); - colTime.prefWidthProperty().bind(historyTable.widthProperty().subtract(18).multiply(0.17)); - colAction.prefWidthProperty().bind(historyTable.widthProperty().subtract(18).multiply(0.18)); + historyTable.getColumns().addAll(colAction, colIndex, colQuery, colFile, colCount, colTime); VBox.setVgrow(historyTable, Priority.ALWAYS); root.getChildren().addAll(filterRow, progressRow, tableTitle, historyTable); @@ -198,9 +193,10 @@ public class MyBatisExportWindow { */ private TableCell buildActionCell() { TableCell cell = new TableCell() { - private final Button openBtn = new Button("打开文件"); - private final Button dirBtn = new Button("打开目录"); + private final Button openBtn = new Button("打开"); + private final Button dirBtn = new Button("目录"); private final HBox btnBox = new HBox(4, openBtn, dirBtn); + { setAlignment(Pos.CENTER); btnBox.setAlignment(Pos.CENTER); } private final ContextMenu menu = new ContextMenu(); private String currentFileName; diff --git a/src/main/java/com/example/workutils/biz/mybatisgenerator/MyBatisGeneratorWindow.java b/src/main/java/com/example/workutils/biz/mybatisgenerator/MyBatisGeneratorWindow.java index 9e76acc..da507ed 100644 --- a/src/main/java/com/example/workutils/biz/mybatisgenerator/MyBatisGeneratorWindow.java +++ b/src/main/java/com/example/workutils/biz/mybatisgenerator/MyBatisGeneratorWindow.java @@ -55,7 +55,6 @@ public class MyBatisGeneratorWindow { private TextField xmlDirField; private TextField mapperDirField; private TreeView projectTree; - private ToggleGroup fillTargetGroup; private ComboBox datasourceCmb; private Label dbUrlLabel; private Label dbUserLabel; @@ -152,50 +151,6 @@ public class MyBatisGeneratorWindow { stage.setScene(scene); } - /** 根据选中的按钮返回对应的输出目录字段 */ - private TextField resolveFillTarget() { - if (fillTargetGroup == null) return null; - Toggle selected = fillTargetGroup.getSelectedToggle(); - if (selected == null) return null; - String type = (String) ((ToggleButton) selected).getUserData(); - if ("model".equals(type)) return modelDirField; - if ("xml".equals(type)) return xmlDirField; - if ("mapper".equals(type)) return mapperDirField; - return null; - } - - private ToggleButton createFillButton(String label, String userData) { - ToggleButton btn = new ToggleButton(label); - btn.setToggleGroup(fillTargetGroup); - btn.setUserData(userData); - btn.setMinWidth(Region.USE_PREF_SIZE); - btn.setStyle("-fx-font-size: " + fsButton() + "px; -fx-cursor: hand; " - + "-fx-padding: 6 16;"); - btn.setOnAction(e -> { - TreeItem selected = projectTree.getSelectionModel().getSelectedItem(); - if (selected != null) { - String path = buildTreePath(selected); - if (path != null && !path.isEmpty()) { - TextField target = resolveFillTarget(); - if (target != null) target.setText(path); - } - } - }); - fillTargetGroup.selectedToggleProperty().addListener((obs, old, cur) -> { - boolean active = (btn == cur); - btn.setStyle(active - ? "-fx-background-color: " + Theme.toWeb(Theme.PRIMARY) + ";-fx-text-fill: white;" - + "-fx-font-size: " + fsButton() + "px; -fx-background-radius: " + Theme.RADIUS_SM + ";" - + "-fx-padding: 6 16;" - : "-fx-background-color: " + Theme.toWeb(Theme.BG_CARD) + ";-fx-text-fill: " + Theme.toWeb(Theme.TEXT_PRIMARY) + ";" - + "-fx-font-size: " + fsButton() + "px; -fx-background-radius: " + Theme.RADIUS_SM + ";" - + "-fx-padding: 6 16;" - + "-fx-border-color: " + Theme.toWeb(Theme.BORDER) + ";-fx-border-width: 1;-fx-border-radius: " + Theme.RADIUS_SM + ";"); - }); - btn.fire(); - return btn; - } - private boolean refreshingDatasource = false; private HBox createDbInfoSection() { @@ -215,7 +170,8 @@ public class MyBatisGeneratorWindow { dsLabel.setMinWidth(Region.USE_PREF_SIZE); datasourceCmb = new ComboBox<>(); - datasourceCmb.setStyle(Theme.STYLE_COMBO + "-fx-font-size: " + fsBody() + "px;"); + datasourceCmb.getStyleClass().add(Theme.CSS_COMBO); + datasourceCmb.setStyle("-fx-font-size: " + fsBody() + "px;"); HBox.setHgrow(datasourceCmb, Priority.ALWAYS); Button refreshDsBtn = StyledButton.small("刷新", Font.font("Microsoft YaHei", fsBody() - 1)); @@ -409,7 +365,7 @@ public class MyBatisGeneratorWindow { patternField = new TextField(); patternField.setPromptText("如 trade%"); patternField.setPrefWidth(200); - patternField.setStyle(Theme.STYLE_TEXT_FIELD); + patternField.getStyleClass().add(Theme.CSS_TEXT_FIELD); Button queryBtn = StyledButton.primary("查询表", Font.font("Microsoft YaHei", fsButton())); queryBtn.setOnAction(e -> onQueryTables()); patternBox.getChildren().addAll(patternField, queryBtn); @@ -442,23 +398,7 @@ public class MyBatisGeneratorWindow { Label dirLabel = new Label("输出目录设置:"); dirLabel.setFont(Theme.heading(fsSection())); dirLabel.setTextFill(Theme.TEXT_PRIMARY); - - this.fillTargetGroup = new ToggleGroup(); - - Label quickLabel = new Label("快速填充:"); - quickLabel.setFont(Font.font("Microsoft YaHei", fsBody())); - quickLabel.setMinWidth(Region.USE_PREF_SIZE); - - ToggleButton btnModel = createFillButton("Model", "model"); - btnModel.setSelected(true); - ToggleButton btnXml = createFillButton("XML", "xml"); - ToggleButton btnMapper = createFillButton("Mapper", "mapper"); - - Region spacer = new Region(); - HBox.setHgrow(spacer, Priority.ALWAYS); - - HBox headerRow = new HBox(Theme.S2, dirLabel, spacer, quickLabel, btnModel, btnXml, btnMapper); - headerRow.setAlignment(Pos.CENTER_LEFT); + box.getChildren().add(dirLabel); HBox modelRow = createDirRow("Model (PO)", fm -> modelDirField = fm); modelDirField = (TextField) modelRow.getChildren().get(1); @@ -469,7 +409,7 @@ public class MyBatisGeneratorWindow { loadDefaultDirs(); - box.getChildren().addAll(headerRow, modelRow, xmlRow, mapperRow); + box.getChildren().addAll(modelRow, xmlRow, mapperRow); return box; } @@ -482,14 +422,25 @@ public class MyBatisGeneratorWindow { lbl.setFont(Font.font("Microsoft YaHei", fsLabel())); TextField field = new TextField(); - field.setStyle(Theme.STYLE_TEXT_FIELD); + field.getStyleClass().add(Theme.CSS_TEXT_FIELD); HBox.setHgrow(field, Priority.ALWAYS); Button browseBtn = StyledButton.small("浏览...", Font.font("Microsoft YaHei", fsBody() - 1)); browseBtn.setOnAction(e -> onBrowseDir(field)); + Button fillBtn = StyledButton.small("填充", Font.font("Microsoft YaHei", fsBody() - 1)); + fillBtn.setOnAction(e -> { + TreeItem selected = projectTree.getSelectionModel().getSelectedItem(); + if (selected != null) { + String path = buildTreePath(selected); + if (path != null && !path.isEmpty()) { + field.setText(path); + } + } + }); + if (fieldRef != null) fieldRef.accept(field); - row.getChildren().addAll(lbl, field, browseBtn); + row.getChildren().addAll(lbl, field, browseBtn, fillBtn); return row; } diff --git a/src/main/java/com/example/workutils/biz/mybatisgenerator/service/generator/MyBatisGeneratorService.java b/src/main/java/com/example/workutils/biz/mybatisgenerator/service/generator/MyBatisGeneratorService.java index 5a1094a..4eae1c9 100644 --- a/src/main/java/com/example/workutils/biz/mybatisgenerator/service/generator/MyBatisGeneratorService.java +++ b/src/main/java/com/example/workutils/biz/mybatisgenerator/service/generator/MyBatisGeneratorService.java @@ -38,6 +38,11 @@ public class MyBatisGeneratorService { public MyBatisGeneratorService(DatasourceConfigManager datasourceMgr, WorkUtilsProperties workUtilsProperties) { this.datasourceMgr = datasourceMgr; this.workUtilsProperties = workUtilsProperties; + // 初始化为用户在系统设置中选择的默认数据源 + this.currentDatasourceName = workUtilsProperties.getDefaultDatasource(); + if (currentDatasourceName != null) { + log.info("默认数据源: {}", currentDatasourceName); + } } // ==================== 数据源切换 ==================== diff --git a/src/main/java/com/example/workutils/biz/scanner/ScannerWindow.java b/src/main/java/com/example/workutils/biz/scanner/ScannerWindow.java index 90ab7f1..be4b265 100644 --- a/src/main/java/com/example/workutils/biz/scanner/ScannerWindow.java +++ b/src/main/java/com/example/workutils/biz/scanner/ScannerWindow.java @@ -97,7 +97,7 @@ public class ScannerWindow { // === 顶部配置卡片(两行布局) === VBox configCard = new VBox(Theme.S2); configCard.setPadding(new Insets(Theme.S3)); - configCard.setStyle(Theme.STYLE_CARD); + configCard.getStyleClass().add(Theme.CSS_CARD); // 第一行:扫描类型 + 正则选项 HBox typeRow = new HBox(Theme.S3 * 2); @@ -122,7 +122,7 @@ public class ScannerWindow { patternLabel.setTextFill(Theme.TEXT_PRIMARY); patternField = new TextField(); patternField.setFont(Theme.base(fsBody())); - patternField.setStyle(Theme.STYLE_TEXT_FIELD); + patternField.getStyleClass().add(Theme.CSS_TEXT_FIELD); patternField.setPromptText("输入关键词或正则表达式"); HBox.setHgrow(patternField, Priority.ALWAYS); @@ -152,7 +152,7 @@ public class ScannerWindow { // === 历史记录表格 === historyTable = new TableView<>(); historyTable.setItems(historyRows); - historyTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); + historyTable.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY); historyTable.setPlaceholder(new Label("暂无扫描记录")); historyTable.setStyle("-fx-background-color: white;" + "-fx-border-color: " + Theme.toWeb(Theme.BORDER) + ";" @@ -177,16 +177,10 @@ public class ScannerWindow { TableColumn colAction = new TableColumn<>("操作"); colAction.setCellValueFactory(new PropertyValueFactory<>("actionLabel")); colAction.setCellFactory(col -> buildActionCell()); + colAction.setPrefWidth(170); + colAction.setResizable(false); - historyTable.getColumns().addAll(colIndex, colQuery, colFile, colCount, colTime, colAction); - - // 列宽百分比自适应:序号4% 查询28% 文件26% 匹配7% 时间17% 操作18% - colIndex.prefWidthProperty().bind(historyTable.widthProperty().subtract(18).multiply(0.04)); - colQuery.prefWidthProperty().bind(historyTable.widthProperty().subtract(18).multiply(0.28)); - colFile.prefWidthProperty().bind(historyTable.widthProperty().subtract(18).multiply(0.26)); - colCount.prefWidthProperty().bind(historyTable.widthProperty().subtract(18).multiply(0.07)); - colTime.prefWidthProperty().bind(historyTable.widthProperty().subtract(18).multiply(0.17)); - colAction.prefWidthProperty().bind(historyTable.widthProperty().subtract(18).multiply(0.18)); + historyTable.getColumns().addAll(colAction, colIndex, colQuery, colFile, colCount, colTime); VBox.setVgrow(historyTable, Priority.ALWAYS); @@ -195,7 +189,7 @@ public class ScannerWindow { logArea.setEditable(false); logArea.setPrefRowCount(5); logArea.setFont(Theme.mono(properties.getFontMono())); - logArea.setStyle(Theme.STYLE_LOG_AREA); + logArea.getStyleClass().add(Theme.CSS_LOG_AREA); root.getChildren().addAll(configCard, progressRow, tableTitle, historyTable, logArea); stage.setScene(new Scene(root)); @@ -206,9 +200,10 @@ public class ScannerWindow { */ private TableCell buildActionCell() { TableCell cell = new TableCell() { - private final Button openBtn = new Button("打开文件"); - private final Button dirBtn = new Button("打开目录"); + private final Button openBtn = new Button("打开"); + private final Button dirBtn = new Button("目录"); private final HBox btnBox = new HBox(4, openBtn, dirBtn); + { setAlignment(Pos.CENTER); btnBox.setAlignment(Pos.CENTER); } private final ContextMenu menu = new ContextMenu(); private String currentFileName; diff --git a/src/main/java/com/example/workutils/system/config/ExternalConfigManager.java b/src/main/java/com/example/workutils/system/config/ExternalConfigManager.java index 40b4819..a7599f1 100644 --- a/src/main/java/com/example/workutils/system/config/ExternalConfigManager.java +++ b/src/main/java/com/example/workutils/system/config/ExternalConfigManager.java @@ -52,11 +52,31 @@ public class ExternalConfigManager { public static final String KEY_MYBATIS_SQLMAPPER_PKG = "mybatis.sqlmapper.package"; // ---- 字体大小(显示设置) ---- - public static final String KEY_FONT_SECTION_TITLE = "font.size.section-title"; // 节标题 默认16 - public static final String KEY_FONT_FIELD_LABEL = "font.size.field-label"; // 字段标签 默认15 - public static final String KEY_FONT_BUTTON = "font.size.button"; // 按钮 默认15 - public static final String KEY_FONT_BODY = "font.size.body"; // 正文/提示 默认14 - public static final String KEY_FONT_MONO = "font.size.mono"; // 等宽/代码 默认14 + public static final String KEY_FONT_H1 = "font.size.h1"; // 页面标题 默认28 + public static final String KEY_FONT_H2 = "font.size.h2"; // 区块标题 默认22 + public static final String KEY_FONT_H3 = "font.size.h3"; // 子标题 默认18 + public static final String KEY_FONT_BODY = "font.size.body"; // 正文 默认15 + public static final String KEY_FONT_SMALL = "font.size.small"; // 辅助文字 默认13 + public static final String KEY_FONT_CAPTION = "font.size.caption"; // 脚注 默认11 + public static final String KEY_FONT_MONO = "font.size.mono"; // 代码 默认14 + public static final String KEY_FONT_BTN_LG = "font.size.btn-lg"; // 主按钮 默认16 + public static final String KEY_FONT_BTN = "font.size.btn"; // 普通按钮 默认14 + public static final String KEY_FONT_BTN_SM = "font.size.btn-sm"; // 小按钮 默认12 + + // 旧键别名(向后兼容,读取 workutils.config 中旧格式) + /** @deprecated 使用 {@link #KEY_FONT_H2} */ + @Deprecated public static final String KEY_FONT_SECTION_TITLE = KEY_FONT_H2; + /** @deprecated 使用 {@link #KEY_FONT_BODY} */ + @Deprecated public static final String KEY_FONT_FIELD_LABEL = KEY_FONT_BODY; + /** @deprecated 使用 {@link #KEY_FONT_BTN} */ + @Deprecated public static final String KEY_FONT_BUTTON = KEY_FONT_BTN; + // KEY_FONT_BODY 和 KEY_FONT_MONO 键名不变,直接复用 + + // ---- 主题 ---- + public static final String KEY_THEME = "theme"; // light | dark + + // ---- 默认数据源 ---- + public static final String KEY_DEFAULT_DATASOURCE = "default.datasource"; /** 配置节定义:名称 → 键列表(MYBATIS 相关配置已迁移到 data/mybatis-generator.properties 独立管理) */ private static final LinkedHashMap SECTIONS = new LinkedHashMap<>(); @@ -69,8 +89,15 @@ public class ExternalConfigManager { KEY_PROJECT_PATHS, KEY_DAMAGED_REPOS }); SECTIONS.put("===== 字体大小(显示设置) =====", new String[]{ - KEY_FONT_SECTION_TITLE, KEY_FONT_FIELD_LABEL, KEY_FONT_BUTTON, - KEY_FONT_BODY, KEY_FONT_MONO + KEY_FONT_H1, KEY_FONT_H2, KEY_FONT_H3, + KEY_FONT_BODY, KEY_FONT_SMALL, KEY_FONT_CAPTION, KEY_FONT_MONO, + KEY_FONT_BTN_LG, KEY_FONT_BTN, KEY_FONT_BTN_SM + }); + SECTIONS.put("===== 界面主题 =====", new String[]{ + KEY_THEME + }); + SECTIONS.put("===== 默认数据源 =====", new String[]{ + KEY_DEFAULT_DATASOURCE }); } diff --git a/src/main/java/com/example/workutils/system/config/SettingsWindow.java b/src/main/java/com/example/workutils/system/config/SettingsWindow.java index 50d30cd..f8fc7fe 100644 --- a/src/main/java/com/example/workutils/system/config/SettingsWindow.java +++ b/src/main/java/com/example/workutils/system/config/SettingsWindow.java @@ -27,20 +27,9 @@ import java.util.Properties; import java.util.concurrent.CountDownLatch; /** - * 系统设置可视化窗口(JavaFX)—— 重构版 + * 系统设置可视化窗口(AtlantaFX 重构版) *

- * 每个 Tab 直接指向 data 目录下的配置文件,用系统默认编辑器打开编辑, - * 各配置文件独立管理,编辑后点击「重新加载」生效。 - *

- * Tab 页组织: - *

    - *
  • 系统路径 — 打开 workutils.config(系统路径 + 字体设置)
  • - *
  • MyBatis 数据库 — 打开 data/mybatis-generator.properties
  • - *
  • 项目路径 — 打开 data/project_paths.txt + 批量字符替换工具
  • - *
  • 多数据源 — 打开 data/datasources.json
  • - *
  • 损坏缓存 — 查看 data/damaged_repos.cache(只读)
  • - *
  • 显示设置 — 字体大小滑块预览
  • - *
+ * 使用 CSS 类控制样式,支持亮色/暗色主题切换。 */ @Slf4j public class SettingsWindow { @@ -51,19 +40,22 @@ public class SettingsWindow { private final Runnable onConfigChanged; private Stage stage; + private Scene scene; // --- 项目路径替换工具 --- private TextField tfReplaceParentDir, tfReplaceAccount; private TextArea taProjectPathsPreview; - // --- 字体大小 --- - private int fontSectionTitle; - private int fontFieldLabel; - private int fontButton; - private int fontBody; - private int fontMono; + // --- 系统默认数据源选择 --- + private ComboBox defaultDsCmb; + private Label dsDetailDriver, dsDetailUrl, dsDetailUser; + + // --- 字体大小(10 级 Token) --- + private int fontH1, fontH2, fontH3; + private int fontBody, fontSmall, fontCaption; + private int fontMono; + private int fontBtnLg, fontBtn, fontBtnSm; - /** 所有动态字体的组件引用,供滑块实时预览 */ private final java.util.List