diff --git a/src/main/java/org/jcnc/jnotepad/plugin/ButtonPlugin.java b/src/main/java/org/jcnc/jnotepad/plugin/ButtonPlugin.java
new file mode 100644
index 0000000..be080c9
--- /dev/null
+++ b/src/main/java/org/jcnc/jnotepad/plugin/ButtonPlugin.java
@@ -0,0 +1,25 @@
+package org.jcnc.jnotepad.plugin;
+
+/**
+ * 新按钮插件
+ *
+ * @author luke
+ */
+public class ButtonPlugin implements Plugin {
+
+ @Override
+ public String getCategoryName() {
+ return "新按钮插件";
+ }
+
+ @Override
+ public String getDisplayName() {
+ return "新按钮";
+ }
+
+ @Override
+ public void execute() {
+ // 在这里实现新按钮插件的逻辑
+ System.out.println("新按钮插件执行了!");
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/jcnc/jnotepad/plugin/Plugin.java b/src/main/java/org/jcnc/jnotepad/plugin/Plugin.java
new file mode 100644
index 0000000..f3ac557
--- /dev/null
+++ b/src/main/java/org/jcnc/jnotepad/plugin/Plugin.java
@@ -0,0 +1,23 @@
+package org.jcnc.jnotepad.plugin;
+
+/**
+ * 插件接口
+ *
+ * 描述插件的基本功能。
+ *
+ * @author luke
+ */
+public interface Plugin extends PluginCategory {
+
+ /**
+ * 获取插件的显示名称
+ *
+ * @return 插件的显示名称
+ */
+ String getDisplayName();
+
+ /**
+ * 执行插件的逻辑
+ */
+ void execute();
+}
diff --git a/src/main/java/org/jcnc/jnotepad/plugin/PluginCategory.java b/src/main/java/org/jcnc/jnotepad/plugin/PluginCategory.java
new file mode 100644
index 0000000..c5c40f7
--- /dev/null
+++ b/src/main/java/org/jcnc/jnotepad/plugin/PluginCategory.java
@@ -0,0 +1,18 @@
+package org.jcnc.jnotepad.plugin;
+
+/**
+ * 插件类别接口
+ *
+ * 描述插件的类别信息。
+ *
+ * @author luke
+ */
+public interface PluginCategory {
+
+ /**
+ * 获取插件类别的名称
+ *
+ * @return 插件类别的名称
+ */
+ String getCategoryName();
+}
diff --git a/src/main/java/org/jcnc/jnotepad/plugin/PluginDemo.java b/src/main/java/org/jcnc/jnotepad/plugin/PluginDemo.java
new file mode 100644
index 0000000..865887c
--- /dev/null
+++ b/src/main/java/org/jcnc/jnotepad/plugin/PluginDemo.java
@@ -0,0 +1,102 @@
+package org.jcnc.jnotepad.plugin;
+
+import javafx.scene.Scene;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.layout.VBox;
+import javafx.stage.FileChooser;
+import javafx.stage.Stage;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 插件演示类
+ *
+ * 用于演示插件加载和执行的界面。
+ *
+ * @author luke
+ */
+public class PluginDemo {
+
+ /**
+ * 启动插件演示界面
+ *
+ * @param primaryStage JavaFX的主舞台
+ */
+ public void start(Stage primaryStage) {
+ PluginManager pluginManager = new PluginManager();
+
+ FileChooser fileChooser = new FileChooser();
+ fileChooser.getExtensionFilters().addAll(
+ new FileChooser.ExtensionFilter("JAR Files", "*.jar")
+ );
+
+ Button loadButton = createLoadButton(primaryStage, fileChooser, pluginManager);
+
+ Button executeButton = new Button("执行插件");
+ executeButton.setOnAction(event -> pluginManager.executePlugins());
+
+ VBox root = new VBox(10, loadButton, executeButton);
+ Scene scene = new Scene(root, 300, 200);
+ primaryStage.setScene(scene);
+ primaryStage.setTitle("插件演示");
+ primaryStage.show();
+ }
+
+ /**
+ * 创建加载插件的按钮
+ *
+ * @param primaryStage JavaFX的主舞台
+ * @param fileChooser 文件选择器
+ * @param pluginManager 插件管理器
+ * @return 加载插件的按钮
+ */
+ private Button createLoadButton(Stage primaryStage, FileChooser fileChooser, PluginManager pluginManager) {
+ Button loadButton = new Button("加载插件");
+ loadButton.setOnAction(event -> {
+ try {
+ File selectedFile = fileChooser.showOpenDialog(primaryStage);
+ if (selectedFile != null) {
+ String pluginFilePath = selectedFile.getAbsolutePath();
+ pluginManager.loadPlugins(pluginFilePath);
+
+ // 更新插件信息显示
+ displayPluginInfo(primaryStage, pluginManager);
+ }
+ } catch (Exception ignored) {
+ }
+ });
+ return loadButton;
+ }
+
+ /**
+ * 显示已加载插件的信息
+ *
+ * @param primaryStage JavaFX的主舞台
+ * @param pluginManager 插件管理器
+ */
+ private void displayPluginInfo(Stage primaryStage, PluginManager pluginManager) {
+ Map> loadedPluginsByCategory = pluginManager.getLoadedPluginsByCategory();
+ VBox infoBox = new VBox();
+
+ for (String category : loadedPluginsByCategory.keySet()) {
+ Label categoryLabel = new Label("类别: " + category);
+ VBox categoryInfoBox = new VBox();
+ List pluginNames = loadedPluginsByCategory.get(category);
+ for (String pluginName : pluginNames) {
+ Label pluginLabel = new Label("插件名称: " + pluginName);
+ categoryInfoBox.getChildren().add(pluginLabel);
+ }
+ infoBox.getChildren().addAll(categoryLabel, categoryInfoBox);
+ }
+
+ Scene infoScene = new Scene(infoBox, 400, 300);
+ Stage infoStage = new Stage();
+ infoStage.setScene(infoScene);
+ infoStage.setTitle("已加载的插件");
+ infoStage.initOwner(primaryStage);
+ infoStage.show();
+ }
+}
diff --git a/src/main/java/org/jcnc/jnotepad/plugin/PluginManager.java b/src/main/java/org/jcnc/jnotepad/plugin/PluginManager.java
new file mode 100644
index 0000000..57aa64a
--- /dev/null
+++ b/src/main/java/org/jcnc/jnotepad/plugin/PluginManager.java
@@ -0,0 +1,73 @@
+package org.jcnc.jnotepad.plugin;
+
+import java.io.File;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 插件管理器
+ *
+ * 该类用于管理插件的加载和执行。
+ * 插件可以通过加载外部JAR文件中的类来扩展应用程序的功能。
+ *
+ * @author luke
+ */
+public class PluginManager {
+ private final List plugins;
+ private final Map> categories;
+
+ /**
+ * 构造方法,初始化插件列表和类别映射
+ */
+ public PluginManager() {
+ plugins = new ArrayList<>();
+ categories = new HashMap<>();
+ }
+
+ /**
+ * 加载插件
+ *
+ * @param pluginFilePath 插件文件的路径
+ * @throws Exception 如果加载插件时发生异常
+ */
+ public void loadPlugins(String pluginFilePath) throws Exception {
+ File file = new File(pluginFilePath);
+
+ if (file.exists() && file.isFile()) {
+ // 创建URLClassLoader以加载Jar文件中的类
+ URLClassLoader classLoader = new URLClassLoader(new URL[]{file.toURI().toURL()});
+ Class> pluginClass = classLoader.loadClass("org.jcnc.jnotepad.plugin.ButtonPlugin");
+ Plugin plugin = (Plugin) pluginClass.getDeclaredConstructor().newInstance();
+ plugins.add(plugin);
+
+ // 添加插件到类别中
+ String categoryName = plugin.getCategoryName();
+ String displayName = plugin.getDisplayName();
+ categories.computeIfAbsent(categoryName, k -> new ArrayList<>()).add(displayName);
+ } else {
+ System.err.println("Plugin file not found: " + pluginFilePath);
+ }
+ }
+
+ /**
+ * 执行加载的插件
+ */
+ public void executePlugins() {
+ for (Plugin plugin : plugins) {
+ plugin.execute();
+ }
+ }
+
+ /**
+ * 获取按类别分类的已加载插件
+ *
+ * @return 插件类别映射
+ */
+ public Map> getLoadedPluginsByCategory() {
+ return categories;
+ }
+}
diff --git a/src/main/java/org/jcnc/jnotepad/root/top/menu/TopMenuBar.java b/src/main/java/org/jcnc/jnotepad/root/top/menu/TopMenuBar.java
index 8e4a218..3edcafa 100644
--- a/src/main/java/org/jcnc/jnotepad/root/top/menu/TopMenuBar.java
+++ b/src/main/java/org/jcnc/jnotepad/root/top/menu/TopMenuBar.java
@@ -10,6 +10,7 @@ import org.jcnc.jnotepad.controller.config.AppConfigController;
import org.jcnc.jnotepad.controller.event.handler.menubar.*;
import org.jcnc.jnotepad.controller.event.handler.tool.SetBtn;
import org.jcnc.jnotepad.controller.i18n.LocalizationController;
+import org.jcnc.jnotepad.plugin.PluginDemo;
import org.jcnc.jnotepad.root.center.main.center.tab.CenterTab;
import org.jcnc.jnotepad.root.center.main.center.tab.CenterTabPane;
import org.jcnc.jnotepad.root.left.sidebar.tools.SidebarToolBar;
@@ -264,6 +265,10 @@ public class TopMenuBar extends MenuBar {
UiResourceBundle.bindStringProperty(pluginMenu.textProperty(), PLUGIN);
addItem = new MenuItem();
+ addItem.setOnAction(event -> {
+ PluginDemo pluginDemo = new PluginDemo();
+ pluginDemo.start(new Stage());
+ });
UiResourceBundle.bindStringProperty(addItem.textProperty(), ADD_PLUGIN);
itemMap.put("addItem", addItem);
diff --git a/tool/jpackage.txt b/tool/jpackage.txt
index 85f61fc..84aa54c 100644
--- a/tool/jpackage.txt
+++ b/tool/jpackage.txt
@@ -4,7 +4,7 @@ jpackage `
-m org.jcnc.jnotepad/org.jcnc.jnotepad.LunchApp `
--runtime-image .\target\JNotepad\ `
--icon src/main/resources/img/icon.ico `
- --app-version 1.1.11 `
+ --app-version 1.1.12 `
--vendor "JCNC"