平台兼容性优化
This commit is contained in:
@@ -6,6 +6,7 @@ import com.example.workutils.system.plugin.PluginDescriptor;
|
|||||||
import com.example.workutils.system.plugin.PluginManager;
|
import com.example.workutils.system.plugin.PluginManager;
|
||||||
import com.example.workutils.system.plugin.VisualPluginFactory;
|
import com.example.workutils.system.plugin.VisualPluginFactory;
|
||||||
import com.example.workutils.system.support.JfxBootstrap;
|
import com.example.workutils.system.support.JfxBootstrap;
|
||||||
|
import com.example.workutils.system.support.SystemOpenHelper;
|
||||||
import com.example.workutils.system.support.WinConsole;
|
import com.example.workutils.system.support.WinConsole;
|
||||||
import com.example.workutils.system.ui.MainWindow;
|
import com.example.workutils.system.ui.MainWindow;
|
||||||
import com.example.workutils.system.ui.Theme;
|
import com.example.workutils.system.ui.Theme;
|
||||||
@@ -82,6 +83,7 @@ public class WorkUtilsApplication implements CommandLineRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
SystemOpenHelper.initialize();
|
||||||
WinConsole.ensureUtf8();
|
WinConsole.ensureUtf8();
|
||||||
SpringApplication.run(WorkUtilsApplication.class, args);
|
SpringApplication.run(WorkUtilsApplication.class, args);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.example.workutils.biz.apidoc.parser.JavaSourceParser;
|
|||||||
import com.example.workutils.system.config.WorkUtilsProperties;
|
import com.example.workutils.system.config.WorkUtilsProperties;
|
||||||
import com.example.workutils.system.support.FileOperationHelper;
|
import com.example.workutils.system.support.FileOperationHelper;
|
||||||
import com.example.workutils.system.support.HistoryPersistence;
|
import com.example.workutils.system.support.HistoryPersistence;
|
||||||
|
import com.example.workutils.system.support.SystemOpenHelper;
|
||||||
import com.example.workutils.system.ui.StyledButton;
|
import com.example.workutils.system.ui.StyledButton;
|
||||||
import com.example.workutils.system.ui.Theme;
|
import com.example.workutils.system.ui.Theme;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
@@ -22,7 +23,6 @@ import javafx.stage.DirectoryChooser;
|
|||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
import java.awt.Desktop;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
@@ -678,7 +678,7 @@ public class ApiDocWindow {
|
|||||||
|
|
||||||
MenuItem defaultOpen = new MenuItem("系统默认程序打开");
|
MenuItem defaultOpen = new MenuItem("系统默认程序打开");
|
||||||
defaultOpen.setOnAction(e -> openFile(currentFileName));
|
defaultOpen.setOnAction(e -> openFile(currentFileName));
|
||||||
MenuItem openNotepad = new MenuItem("用记事本打开");
|
MenuItem openNotepad = new MenuItem("用系统编辑器打开");
|
||||||
openNotepad.setOnAction(e -> openWithNotepad(currentFileName));
|
openNotepad.setOnAction(e -> openWithNotepad(currentFileName));
|
||||||
MenuItem openFolder = new MenuItem("打开所在文件夹");
|
MenuItem openFolder = new MenuItem("打开所在文件夹");
|
||||||
openFolder.setOnAction(e -> openFolder(currentFileName));
|
openFolder.setOnAction(e -> openFolder(currentFileName));
|
||||||
@@ -728,12 +728,7 @@ public class ApiDocWindow {
|
|||||||
try {
|
try {
|
||||||
File f = new File(path);
|
File f = new File(path);
|
||||||
if (!f.exists()) return;
|
if (!f.exists()) return;
|
||||||
String os = System.getProperty("os.name").toLowerCase();
|
SystemOpenHelper.openFile(f.toPath());
|
||||||
if (os.contains("win")) {
|
|
||||||
new ProcessBuilder("cmd", "/c", "start", "", path).start();
|
|
||||||
} else {
|
|
||||||
Desktop.getDesktop().open(f);
|
|
||||||
}
|
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -741,7 +736,7 @@ public class ApiDocWindow {
|
|||||||
private void openWithNotepad(String path) {
|
private void openWithNotepad(String path) {
|
||||||
if (path == null) return;
|
if (path == null) return;
|
||||||
try {
|
try {
|
||||||
new ProcessBuilder("notepad.exe", path).start();
|
SystemOpenHelper.editFile(path);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
openFile(path);
|
openFile(path);
|
||||||
}
|
}
|
||||||
@@ -752,12 +747,7 @@ public class ApiDocWindow {
|
|||||||
try {
|
try {
|
||||||
File f = new File(path).getParentFile();
|
File f = new File(path).getParentFile();
|
||||||
if (f == null || !f.exists()) return;
|
if (f == null || !f.exists()) return;
|
||||||
String os = System.getProperty("os.name").toLowerCase();
|
SystemOpenHelper.revealInFileManager(path);
|
||||||
if (os.contains("win")) {
|
|
||||||
new ProcessBuilder("explorer.exe", "/select,", path).start();
|
|
||||||
} else {
|
|
||||||
Desktop.getDesktop().open(f);
|
|
||||||
}
|
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import java.io.InputStreamReader;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -543,27 +544,27 @@ public class GitSyncPlugin implements Plugin, InitializingBean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 1. fetch
|
// 1. fetch
|
||||||
CmdResult r1 = runGit(repoPath, "git fetch origin " + branch);
|
CmdResult r1 = runGit(repoPath, "git", "fetch", "origin", branch);
|
||||||
if (!r1.ok) {
|
if (!r1.ok) {
|
||||||
log.warn("fetch 失败: {} -> {}", repoPath, r1.error);
|
log.warn("fetch 失败: {} -> {}", repoPath, r1.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. checkout:先 stash 本地修改,避免因未提交修改导致 checkout 失败
|
// 2. checkout:先 stash 本地修改,避免因未提交修改导致 checkout 失败
|
||||||
boolean didStash = false;
|
boolean didStash = false;
|
||||||
CmdResult r2 = runGit(repoPath, "git checkout " + branch);
|
CmdResult r2 = runGit(repoPath, "git", "checkout", branch);
|
||||||
if (!r2.ok) {
|
if (!r2.ok) {
|
||||||
// checkout 失败常见原因:有未提交修改 → stash 后重试
|
// checkout 失败常见原因:有未提交修改 → stash 后重试
|
||||||
CmdResult stashResult = runGit(repoPath, "git stash push -m \"workutils-auto-stash\"");
|
CmdResult stashResult = runGit(repoPath, "git", "stash", "push", "-m", "workutils-auto-stash");
|
||||||
if (stashResult.ok) {
|
if (stashResult.ok) {
|
||||||
didStash = true;
|
didStash = true;
|
||||||
r2 = runGit(repoPath, "git checkout " + branch);
|
r2 = runGit(repoPath, "git", "checkout", branch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!r2.ok) {
|
if (!r2.ok) {
|
||||||
log.warn("checkout 失败: {} -> {},标记为损坏", repoPath, r2.error);
|
log.warn("checkout 失败: {} -> {},标记为损坏", repoPath, r2.error);
|
||||||
// 如果 stash 了但 checkout 仍失败,恢复 stash
|
// 如果 stash 了但 checkout 仍失败,恢复 stash
|
||||||
if (didStash) {
|
if (didStash) {
|
||||||
runGit(repoPath, "git stash pop");
|
runGit(repoPath, "git", "stash", "pop");
|
||||||
}
|
}
|
||||||
SyncResult r = SyncResult.DAMAGED;
|
SyncResult r = SyncResult.DAMAGED;
|
||||||
r.error = "checkout 失败: " + r2.error;
|
r.error = "checkout 失败: " + r2.error;
|
||||||
@@ -572,12 +573,12 @@ public class GitSyncPlugin implements Plugin, InitializingBean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3. pull
|
// 3. pull
|
||||||
CmdResult r3 = runGit(repoPath, "git pull origin " + branch);
|
CmdResult r3 = runGit(repoPath, "git", "pull", "origin", branch);
|
||||||
if (!r3.ok) {
|
if (!r3.ok) {
|
||||||
log.warn("pull 失败: {} -> {},标记为损坏", repoPath, r3.error);
|
log.warn("pull 失败: {} -> {},标记为损坏", repoPath, r3.error);
|
||||||
// pull 失败时恢复 stash(如果 stash 过)
|
// pull 失败时恢复 stash(如果 stash 过)
|
||||||
if (didStash) {
|
if (didStash) {
|
||||||
runGit(repoPath, "git stash pop");
|
runGit(repoPath, "git", "stash", "pop");
|
||||||
}
|
}
|
||||||
SyncResult r = SyncResult.DAMAGED;
|
SyncResult r = SyncResult.DAMAGED;
|
||||||
r.error = "pull 失败: " + r3.error;
|
r.error = "pull 失败: " + r3.error;
|
||||||
@@ -587,7 +588,7 @@ public class GitSyncPlugin implements Plugin, InitializingBean {
|
|||||||
|
|
||||||
// 4. 恢复之前 stash 的修改(忽略冲突,保留 stash 让用户手动处理)
|
// 4. 恢复之前 stash 的修改(忽略冲突,保留 stash 让用户手动处理)
|
||||||
if (didStash) {
|
if (didStash) {
|
||||||
CmdResult popResult = runGit(repoPath, "git stash pop");
|
CmdResult popResult = runGit(repoPath, "git", "stash", "pop");
|
||||||
if (!popResult.ok) {
|
if (!popResult.ok) {
|
||||||
log.warn("stash pop 有冲突: {} -> {},已保留在 stash 中", repoPath, popResult.error);
|
log.warn("stash pop 有冲突: {} -> {},已保留在 stash 中", repoPath, popResult.error);
|
||||||
}
|
}
|
||||||
@@ -619,8 +620,7 @@ public class GitSyncPlugin implements Plugin, InitializingBean {
|
|||||||
}
|
}
|
||||||
// clone
|
// clone
|
||||||
String parentDir = dir.getParent() != null ? dir.getParent() : ".";
|
String parentDir = dir.getParent() != null ? dir.getParent() : ".";
|
||||||
CmdResult cr = runGit(parentDir,
|
CmdResult cr = runGit(parentDir, "git", "clone", "-b", branch, cloneUrl, repoPath);
|
||||||
"git clone -b " + branch + " " + cloneUrl + " \"" + repoPath + "\"");
|
|
||||||
if (cr.ok) {
|
if (cr.ok) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@@ -649,16 +649,15 @@ public class GitSyncPlugin implements Plugin, InitializingBean {
|
|||||||
* 关键:进程输出在独立线程中读取,主线程通过 process.waitFor(timeout) 等待。
|
* 关键:进程输出在独立线程中读取,主线程通过 process.waitFor(timeout) 等待。
|
||||||
* 超时后强制杀进程 + 中断读线程,杜绝僵尸进程 / 永久阻塞。
|
* 超时后强制杀进程 + 中断读线程,杜绝僵尸进程 / 永久阻塞。
|
||||||
*/
|
*/
|
||||||
private CmdResult runGit(String workingDir, String command) {
|
private CmdResult runGit(String workingDir, String... command) {
|
||||||
CmdResult result = new CmdResult();
|
CmdResult result = new CmdResult();
|
||||||
Process process = null;
|
Process process = null;
|
||||||
Thread readerThread = null;
|
Thread readerThread = null;
|
||||||
StringBuilder output = new StringBuilder();
|
StringBuilder output = new StringBuilder();
|
||||||
|
String commandText = String.join(" ", Arrays.asList(command));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ProcessBuilder pb = isWindows()
|
ProcessBuilder pb = new ProcessBuilder(command);
|
||||||
? new ProcessBuilder("cmd.exe", "/c", command)
|
|
||||||
: new ProcessBuilder("bash", "-c", command);
|
|
||||||
|
|
||||||
if (workingDir != null) {
|
if (workingDir != null) {
|
||||||
pb.directory(new File(workingDir));
|
pb.directory(new File(workingDir));
|
||||||
@@ -685,7 +684,7 @@ public class GitSyncPlugin implements Plugin, InitializingBean {
|
|||||||
// 等待进程结束(带超时)
|
// 等待进程结束(带超时)
|
||||||
boolean finished = process.waitFor(CMD_TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
boolean finished = process.waitFor(CMD_TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||||
if (!finished) {
|
if (!finished) {
|
||||||
log.warn("Git 命令超时({}s): {} -> {}", CMD_TIMEOUT_SECONDS, workingDir, command);
|
log.warn("Git 命令超时({}s): {} -> {}", CMD_TIMEOUT_SECONDS, workingDir, commandText);
|
||||||
result.ok = false;
|
result.ok = false;
|
||||||
result.error = "命令超时(" + CMD_TIMEOUT_SECONDS + "s)";
|
result.error = "命令超时(" + CMD_TIMEOUT_SECONDS + "s)";
|
||||||
return result;
|
return result;
|
||||||
@@ -728,7 +727,7 @@ public class GitSyncPlugin implements Plugin, InitializingBean {
|
|||||||
|
|
||||||
private String getRemoteUrl(String repoPath, Map<String, String> baseUrlMap) {
|
private String getRemoteUrl(String repoPath, Map<String, String> baseUrlMap) {
|
||||||
// 1. 尝试 git remote get-url
|
// 1. 尝试 git remote get-url
|
||||||
CmdResult r = runGit(repoPath, "git remote get-url origin");
|
CmdResult r = runGit(repoPath, "git", "remote", "get-url", "origin");
|
||||||
if (r.ok && !r.output.isEmpty()) {
|
if (r.ok && !r.output.isEmpty()) {
|
||||||
return r.output.trim();
|
return r.output.trim();
|
||||||
}
|
}
|
||||||
@@ -800,14 +799,21 @@ public class GitSyncPlugin implements Plugin, InitializingBean {
|
|||||||
return baseUrlMap.get(normalizePath(repoPath));
|
return baseUrlMap.get(normalizePath(repoPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 路径规范化:统一用 / 分隔、小写,用于映射表匹配 */
|
/** 路径规范化:统一用 / 分隔,不改变大小写,避免破坏大小写敏感文件系统。 */
|
||||||
private String normalizePath(String path) {
|
private String normalizePath(String path) {
|
||||||
if (path == null || path.isEmpty()) return "";
|
if (path == null || path.isEmpty()) return "";
|
||||||
return path.replace('\\', '/').toLowerCase();
|
String normalized = path.trim().replace('\\', '/');
|
||||||
|
while (normalized.length() > 1 && normalized.endsWith("/") && !isWindowsDriveRoot(normalized)) {
|
||||||
|
normalized = normalized.substring(0, normalized.length() - 1);
|
||||||
|
}
|
||||||
|
return normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isWindows() {
|
private boolean isWindowsDriveRoot(String path) {
|
||||||
return System.getProperty("os.name").toLowerCase().contains("win");
|
return path.length() == 3
|
||||||
|
&& Character.isLetter(path.charAt(0))
|
||||||
|
&& path.charAt(1) == ':'
|
||||||
|
&& path.charAt(2) == '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 供可视化窗口调用的公开 API ====================
|
// ==================== 供可视化窗口调用的公开 API ====================
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.example.workutils.biz.mybatisexport;
|
|||||||
import com.example.workutils.biz.mybatisexport.service.MyBatisExportService;
|
import com.example.workutils.biz.mybatisexport.service.MyBatisExportService;
|
||||||
import com.example.workutils.system.config.WorkUtilsProperties;
|
import com.example.workutils.system.config.WorkUtilsProperties;
|
||||||
import com.example.workutils.system.support.HistoryPersistence;
|
import com.example.workutils.system.support.HistoryPersistence;
|
||||||
|
import com.example.workutils.system.support.SystemOpenHelper;
|
||||||
import com.example.workutils.system.ui.StyledButton;
|
import com.example.workutils.system.ui.StyledButton;
|
||||||
import com.example.workutils.system.ui.Theme;
|
import com.example.workutils.system.ui.Theme;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
@@ -17,7 +18,6 @@ import javafx.scene.layout.*;
|
|||||||
import javafx.scene.Parent;
|
import javafx.scene.Parent;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
import java.awt.Desktop;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -242,7 +242,7 @@ public class MyBatisExportWindow {
|
|||||||
// 右键菜单选项
|
// 右键菜单选项
|
||||||
MenuItem defaultOpen = new MenuItem("系统默认程序打开");
|
MenuItem defaultOpen = new MenuItem("系统默认程序打开");
|
||||||
defaultOpen.setOnAction(e -> openFile(currentFileName));
|
defaultOpen.setOnAction(e -> openFile(currentFileName));
|
||||||
MenuItem openNotepad = new MenuItem("用记事本打开");
|
MenuItem openNotepad = new MenuItem("用系统编辑器打开");
|
||||||
openNotepad.setOnAction(e -> openWithNotepad(currentFileName));
|
openNotepad.setOnAction(e -> openWithNotepad(currentFileName));
|
||||||
MenuItem openFolder = new MenuItem("打开所在文件夹");
|
MenuItem openFolder = new MenuItem("打开所在文件夹");
|
||||||
openFolder.setOnAction(e -> openFolder(currentFileName));
|
openFolder.setOnAction(e -> openFolder(currentFileName));
|
||||||
@@ -428,12 +428,7 @@ public class MyBatisExportWindow {
|
|||||||
try {
|
try {
|
||||||
File f = new File(path);
|
File f = new File(path);
|
||||||
if (!f.exists()) return;
|
if (!f.exists()) return;
|
||||||
String os = System.getProperty("os.name").toLowerCase();
|
SystemOpenHelper.openFile(f.toPath());
|
||||||
if (os.contains("win")) {
|
|
||||||
new ProcessBuilder("cmd", "/c", "start", "", path).start();
|
|
||||||
} else {
|
|
||||||
Desktop.getDesktop().open(f);
|
|
||||||
}
|
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -441,7 +436,7 @@ public class MyBatisExportWindow {
|
|||||||
private void openWithNotepad(String path) {
|
private void openWithNotepad(String path) {
|
||||||
if (path == null) return;
|
if (path == null) return;
|
||||||
try {
|
try {
|
||||||
new ProcessBuilder("notepad.exe", path).start();
|
SystemOpenHelper.editFile(path);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
openFile(path);
|
openFile(path);
|
||||||
}
|
}
|
||||||
@@ -452,12 +447,7 @@ public class MyBatisExportWindow {
|
|||||||
try {
|
try {
|
||||||
File f = new File(path).getParentFile();
|
File f = new File(path).getParentFile();
|
||||||
if (f == null || !f.exists()) return;
|
if (f == null || !f.exists()) return;
|
||||||
String os = System.getProperty("os.name").toLowerCase();
|
SystemOpenHelper.revealInFileManager(path);
|
||||||
if (os.contains("win")) {
|
|
||||||
new ProcessBuilder("explorer.exe", "/select,", path).start();
|
|
||||||
} else {
|
|
||||||
Desktop.getDesktop().open(f);
|
|
||||||
}
|
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import com.example.workutils.system.config.WorkUtilsProperties;
|
|||||||
import com.example.workutils.system.support.FileOperationHelper;
|
import com.example.workutils.system.support.FileOperationHelper;
|
||||||
import com.example.workutils.system.support.HistoryPersistence;
|
import com.example.workutils.system.support.HistoryPersistence;
|
||||||
import com.example.workutils.system.support.OutputHelper;
|
import com.example.workutils.system.support.OutputHelper;
|
||||||
|
import com.example.workutils.system.support.SystemOpenHelper;
|
||||||
import com.example.workutils.system.ui.StyledButton;
|
import com.example.workutils.system.ui.StyledButton;
|
||||||
import com.example.workutils.system.ui.Theme;
|
import com.example.workutils.system.ui.Theme;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
@@ -19,7 +20,6 @@ import javafx.scene.layout.*;
|
|||||||
import javafx.scene.Parent;
|
import javafx.scene.Parent;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
import java.awt.Desktop;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -222,7 +222,7 @@ public class ScannerWindow {
|
|||||||
// 右键菜单选项
|
// 右键菜单选项
|
||||||
MenuItem defaultOpen = new MenuItem("系统默认程序打开");
|
MenuItem defaultOpen = new MenuItem("系统默认程序打开");
|
||||||
defaultOpen.setOnAction(e -> openFile(currentFileName));
|
defaultOpen.setOnAction(e -> openFile(currentFileName));
|
||||||
MenuItem openNotepad = new MenuItem("用记事本打开");
|
MenuItem openNotepad = new MenuItem("用系统编辑器打开");
|
||||||
openNotepad.setOnAction(e -> openWithNotepad(currentFileName));
|
openNotepad.setOnAction(e -> openWithNotepad(currentFileName));
|
||||||
MenuItem openFolder = new MenuItem("打开所在文件夹");
|
MenuItem openFolder = new MenuItem("打开所在文件夹");
|
||||||
openFolder.setOnAction(e -> openFolder(currentFileName));
|
openFolder.setOnAction(e -> openFolder(currentFileName));
|
||||||
@@ -372,13 +372,8 @@ public class ScannerWindow {
|
|||||||
logArea.appendText("[WARN] 文件不存在: " + path + "\n");
|
logArea.appendText("[WARN] 文件不存在: " + path + "\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Windows 用 cmd /c start 更可靠
|
// Delegate platform-specific open behavior to SystemOpenHelper.
|
||||||
String os = System.getProperty("os.name").toLowerCase();
|
SystemOpenHelper.openFile(f.toPath());
|
||||||
if (os.contains("win")) {
|
|
||||||
new ProcessBuilder("cmd", "/c", "start", "", path).start();
|
|
||||||
} else {
|
|
||||||
Desktop.getDesktop().open(f);
|
|
||||||
}
|
|
||||||
logArea.appendText("[OK] 已打开: " + path + "\n");
|
logArea.appendText("[OK] 已打开: " + path + "\n");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logArea.appendText("[ERROR] 打开失败: " + (e.getMessage() != null ? e.getMessage() : e.getClass().getSimpleName()) + "\n");
|
logArea.appendText("[ERROR] 打开失败: " + (e.getMessage() != null ? e.getMessage() : e.getClass().getSimpleName()) + "\n");
|
||||||
@@ -387,8 +382,8 @@ public class ScannerWindow {
|
|||||||
|
|
||||||
private void openWithNotepad(String path) {
|
private void openWithNotepad(String path) {
|
||||||
try {
|
try {
|
||||||
new ProcessBuilder("notepad.exe", path).start();
|
SystemOpenHelper.editFile(path);
|
||||||
logArea.appendText("[OK] 用记事本打开: " + path + "\n");
|
logArea.appendText("[OK] 用系统编辑器打开: " + path + "\n");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
openFile(path); // fallback
|
openFile(path); // fallback
|
||||||
}
|
}
|
||||||
@@ -405,12 +400,7 @@ public class ScannerWindow {
|
|||||||
logArea.appendText("[WARN] 目录不存在: " + path + "\n");
|
logArea.appendText("[WARN] 目录不存在: " + path + "\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String os = System.getProperty("os.name").toLowerCase();
|
SystemOpenHelper.revealInFileManager(path);
|
||||||
if (os.contains("win")) {
|
|
||||||
new ProcessBuilder("explorer.exe", "/select,", path).start();
|
|
||||||
} else {
|
|
||||||
Desktop.getDesktop().open(f);
|
|
||||||
}
|
|
||||||
logArea.appendText("[OK] 已打开目录: " + f.getAbsolutePath() + "\n");
|
logArea.appendText("[OK] 已打开目录: " + f.getAbsolutePath() + "\n");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logArea.appendText("[ERROR] 打开目录失败: " + (e.getMessage() != null ? e.getMessage() : e.getClass().getSimpleName()) + "\n");
|
logArea.appendText("[ERROR] 打开目录失败: " + (e.getMessage() != null ? e.getMessage() : e.getClass().getSimpleName()) + "\n");
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.example.workutils.system.config;
|
package com.example.workutils.system.config;
|
||||||
|
|
||||||
import com.example.workutils.system.support.JfxBootstrap;
|
import com.example.workutils.system.support.JfxBootstrap;
|
||||||
|
import com.example.workutils.system.support.SystemOpenHelper;
|
||||||
import com.example.workutils.system.ui.Theme;
|
import com.example.workutils.system.ui.Theme;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
@@ -18,7 +19,6 @@ import javafx.stage.Screen;
|
|||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.awt.Desktop;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
@@ -205,7 +205,7 @@ public class SettingsWindow {
|
|||||||
HBox btnRow = new HBox(10);
|
HBox btnRow = new HBox(10);
|
||||||
btnRow.setAlignment(Pos.CENTER_LEFT);
|
btnRow.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
Button btnOpen = new Button(isReadOnly ? "用记事本查看" : "用记事本打开");
|
Button btnOpen = new Button(isReadOnly ? "用系统编辑器查看" : "用系统编辑器打开");
|
||||||
btnOpen.setFont(font(fontBtn));
|
btnOpen.setFont(font(fontBtn));
|
||||||
btnOpen.getStyleClass().add(Theme.CSS_BTN_PRIMARY);
|
btnOpen.getStyleClass().add(Theme.CSS_BTN_PRIMARY);
|
||||||
allButtons.add(btnOpen);
|
allButtons.add(btnOpen);
|
||||||
@@ -293,7 +293,7 @@ public class SettingsWindow {
|
|||||||
HBox btnRow = new HBox(10);
|
HBox btnRow = new HBox(10);
|
||||||
btnRow.setAlignment(Pos.CENTER_LEFT);
|
btnRow.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
Button btnOpen = new Button("用记事本打开");
|
Button btnOpen = new Button("用系统编辑器打开");
|
||||||
btnOpen.setFont(font(fontBtn));
|
btnOpen.setFont(font(fontBtn));
|
||||||
btnOpen.getStyleClass().add(Theme.CSS_BTN_PRIMARY);
|
btnOpen.getStyleClass().add(Theme.CSS_BTN_PRIMARY);
|
||||||
allButtons.add(btnOpen);
|
allButtons.add(btnOpen);
|
||||||
@@ -421,7 +421,7 @@ public class SettingsWindow {
|
|||||||
HBox fileBtnRow = new HBox(10);
|
HBox fileBtnRow = new HBox(10);
|
||||||
fileBtnRow.setAlignment(Pos.CENTER_LEFT);
|
fileBtnRow.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
Button btnOpen = new Button("用记事本编辑");
|
Button btnOpen = new Button("用系统编辑器编辑");
|
||||||
btnOpen.setFont(font(fontBtn));
|
btnOpen.setFont(font(fontBtn));
|
||||||
btnOpen.getStyleClass().add(Theme.CSS_BTN_PRIMARY);
|
btnOpen.getStyleClass().add(Theme.CSS_BTN_PRIMARY);
|
||||||
allButtons.add(btnOpen);
|
allButtons.add(btnOpen);
|
||||||
@@ -553,7 +553,7 @@ public class SettingsWindow {
|
|||||||
HBox btnRow = new HBox(10);
|
HBox btnRow = new HBox(10);
|
||||||
btnRow.setAlignment(Pos.CENTER_LEFT);
|
btnRow.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
Button btnView = new Button("用记事本查看");
|
Button btnView = new Button("用系统编辑器查看");
|
||||||
btnView.setFont(font(fontBtn));
|
btnView.setFont(font(fontBtn));
|
||||||
btnView.getStyleClass().add(Theme.CSS_BTN_PRIMARY);
|
btnView.getStyleClass().add(Theme.CSS_BTN_PRIMARY);
|
||||||
allButtons.add(btnView);
|
allButtons.add(btnView);
|
||||||
@@ -986,11 +986,7 @@ public class SettingsWindow {
|
|||||||
Files.createFile(file);
|
Files.createFile(file);
|
||||||
log.info("自动创建文件: {}", file.toAbsolutePath());
|
log.info("自动创建文件: {}", file.toAbsolutePath());
|
||||||
}
|
}
|
||||||
if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.EDIT)) {
|
SystemOpenHelper.editFile(file);
|
||||||
Desktop.getDesktop().edit(file.toFile());
|
|
||||||
} else {
|
|
||||||
Runtime.getRuntime().exec(new String[]{"notepad.exe", file.toAbsolutePath().toString()});
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("打开文件失败: {}", file.toAbsolutePath(), e);
|
log.error("打开文件失败: {}", file.toAbsolutePath(), e);
|
||||||
showAlert(AlertType.ERROR, "打开文件失败: " + e.getMessage());
|
showAlert(AlertType.ERROR, "打开文件失败: " + e.getMessage());
|
||||||
|
|||||||
@@ -0,0 +1,191 @@
|
|||||||
|
package com.example.workutils.system.support;
|
||||||
|
|
||||||
|
import java.awt.Desktop;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public final class SystemOpenHelper {
|
||||||
|
|
||||||
|
private static final OperatingSystem CURRENT_OS = detectOs();
|
||||||
|
|
||||||
|
private SystemOpenHelper() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void initialize() {
|
||||||
|
currentOs();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OperatingSystem currentOs() {
|
||||||
|
return CURRENT_OS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isWindows() {
|
||||||
|
return CURRENT_OS == OperatingSystem.WINDOWS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isMac() {
|
||||||
|
return CURRENT_OS == OperatingSystem.MAC;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void openFile(String path) throws IOException {
|
||||||
|
if (path == null || path.trim().isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
openFile(Path.of(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void openFile(Path path) throws IOException {
|
||||||
|
if (path == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
File file = path.toFile();
|
||||||
|
if (!file.exists()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (tryDesktopOpen(file)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
runOpenFallback(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void editFile(String path) throws IOException {
|
||||||
|
if (path == null || path.trim().isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
editFile(Path.of(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void editFile(Path path) throws IOException {
|
||||||
|
if (path == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
File file = path.toFile();
|
||||||
|
if (!file.exists()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (tryDesktopEdit(file)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (CURRENT_OS) {
|
||||||
|
case WINDOWS:
|
||||||
|
start("notepad.exe", file.getAbsolutePath());
|
||||||
|
break;
|
||||||
|
case MAC:
|
||||||
|
start("open", "-e", file.getAbsolutePath());
|
||||||
|
break;
|
||||||
|
case LINUX:
|
||||||
|
case OTHER:
|
||||||
|
default:
|
||||||
|
runOpenFallback(file);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void revealInFileManager(String path) throws IOException {
|
||||||
|
if (path == null || path.trim().isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
revealInFileManager(Path.of(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void revealInFileManager(Path path) throws IOException {
|
||||||
|
if (path == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
File target = path.toFile();
|
||||||
|
File parent = target.isDirectory() ? target : target.getParentFile();
|
||||||
|
if (parent == null || !parent.exists()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (CURRENT_OS) {
|
||||||
|
case WINDOWS:
|
||||||
|
if (target.exists() && target.isFile()) {
|
||||||
|
start("explorer.exe", "/select," + target.getAbsolutePath());
|
||||||
|
} else {
|
||||||
|
start("explorer.exe", parent.getAbsolutePath());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MAC:
|
||||||
|
if (target.exists()) {
|
||||||
|
start("open", "-R", target.getAbsolutePath());
|
||||||
|
} else {
|
||||||
|
start("open", parent.getAbsolutePath());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LINUX:
|
||||||
|
case OTHER:
|
||||||
|
default:
|
||||||
|
if (!tryDesktopOpen(parent)) {
|
||||||
|
start("xdg-open", parent.getAbsolutePath());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static OperatingSystem detectOs() {
|
||||||
|
String osName = System.getProperty("os.name", "").toLowerCase(Locale.ROOT);
|
||||||
|
if (osName.contains("win")) {
|
||||||
|
return OperatingSystem.WINDOWS;
|
||||||
|
}
|
||||||
|
if (osName.contains("mac") || osName.contains("darwin")) {
|
||||||
|
return OperatingSystem.MAC;
|
||||||
|
}
|
||||||
|
if (osName.contains("nux") || osName.contains("nix") || osName.contains("aix")) {
|
||||||
|
return OperatingSystem.LINUX;
|
||||||
|
}
|
||||||
|
return OperatingSystem.OTHER;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean tryDesktopOpen(File file) {
|
||||||
|
try {
|
||||||
|
if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.OPEN)) {
|
||||||
|
Desktop.getDesktop().open(file);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (IOException | RuntimeException ignored) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean tryDesktopEdit(File file) {
|
||||||
|
try {
|
||||||
|
if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.EDIT)) {
|
||||||
|
Desktop.getDesktop().edit(file);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (IOException | RuntimeException ignored) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void runOpenFallback(File file) throws IOException {
|
||||||
|
switch (CURRENT_OS) {
|
||||||
|
case WINDOWS:
|
||||||
|
start("cmd", "/c", "start", "", file.getAbsolutePath());
|
||||||
|
break;
|
||||||
|
case MAC:
|
||||||
|
start("open", file.getAbsolutePath());
|
||||||
|
break;
|
||||||
|
case LINUX:
|
||||||
|
case OTHER:
|
||||||
|
default:
|
||||||
|
start("xdg-open", file.getAbsolutePath());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void start(String... command) throws IOException {
|
||||||
|
new ProcessBuilder(command).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum OperatingSystem {
|
||||||
|
WINDOWS,
|
||||||
|
MAC,
|
||||||
|
LINUX,
|
||||||
|
OTHER
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user