diff --git a/src/main/java/com/light/GitManagerApp.java b/src/main/java/com/light/GitManagerApp.java index 8688f451893a10e0e531a007015637e49d84b823..b341d43dda339ed59a6023e6ebd507bfa3bb260e 100644 --- a/src/main/java/com/light/GitManagerApp.java +++ b/src/main/java/com/light/GitManagerApp.java @@ -6,10 +6,9 @@ import com.light.layout.ContentPane; import com.light.layout.MenuPane; import com.light.thread.AsyncTask; import com.light.thread.GitThreadPool; -import com.light.util.FxDataUtil; +import com.light.util.FxApplicationContextUtils; import com.light.util.FxUtil; import com.light.util.H2PoolUtils; -import com.light.util.NodeUtils; import javafx.application.Application; import javafx.geometry.Insets; import javafx.scene.Scene; @@ -21,8 +20,8 @@ import javafx.stage.Stage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static com.light.util.FxDataUtil.THEME_LIST; -import static com.light.util.NodeUtils.MAIN_MODAL_ID; +import static com.light.util.FxApplicationContextUtils.THEME_LIST; +import static com.light.util.FxUtil.MAIN_MODAL_ID; public class GitManagerApp extends Application { @@ -30,39 +29,35 @@ public class GitManagerApp extends Application { private static final String STYLE_SHEET = FxUtil.getResource("/css/root.css"); - private Scene scene; private long startTime; @Override public void init() throws Exception { startTime = System.currentTimeMillis(); super.init(); - AsyncTask.runOnce(new AsyncTask("data_init", "初始化数据库和主题数据") { - @Override - protected void handler() throws Exception { - String gitDbInit = H2PoolUtils.queryDictByLabel("GIT_DB_INIT", "0"); - if ("0".equals(gitDbInit)) { - H2PoolUtils.createTable(); - H2PoolUtils.initGitProjectDictData(); - } - - THEME_LIST.add(new PrimerLight()); - THEME_LIST.add(new PrimerDark()); - THEME_LIST.add(new NordLight()); - THEME_LIST.add(new NordDark()); - THEME_LIST.add(new CupertinoLight()); - THEME_LIST.add(new CupertinoDark()); - THEME_LIST.add(new Dracula()); - - // 设置主题 - String themeName = H2PoolUtils.queryDictByLabel("GIT_CURRENT_THEME", "Primer Light"); - FxDataUtil.CURRENT_THEME_NAME.set(themeName); - THEME_LIST.stream().filter(theme -> theme.getName().equals(themeName)) - .findFirst() - .ifPresentOrElse(theme -> Application.setUserAgentStylesheet(theme.getUserAgentStylesheet()), - () -> Application.setUserAgentStylesheet(THEME_LIST.getFirst().getUserAgentStylesheet()) - ); + AsyncTask.runOnce("初始化数据库和主题数据", () -> { + String gitDbInit = H2PoolUtils.queryDictByLabel("GIT_DB_INIT", "0"); + if ("0".equals(gitDbInit)) { + H2PoolUtils.createTable(); + H2PoolUtils.initGitProjectDictData(); } + + THEME_LIST.add(new PrimerLight()); + THEME_LIST.add(new PrimerDark()); + THEME_LIST.add(new NordLight()); + THEME_LIST.add(new NordDark()); + THEME_LIST.add(new CupertinoLight()); + THEME_LIST.add(new CupertinoDark()); + THEME_LIST.add(new Dracula()); + + // 设置主题 + String themeName = H2PoolUtils.queryDictByLabel("GIT_CURRENT_THEME", "Primer Light"); + FxApplicationContextUtils.CURRENT_THEME_NAME.set(themeName); + THEME_LIST.stream().filter(theme -> theme.getName().equals(themeName)) + .findFirst() + .ifPresentOrElse(theme -> Application.setUserAgentStylesheet(theme.getUserAgentStylesheet()), + () -> Application.setUserAgentStylesheet(THEME_LIST.getFirst().getUserAgentStylesheet()) + ); }); } @@ -86,10 +81,10 @@ public class GitManagerApp extends Application { HBox.setHgrow(contentPane, Priority.ALWAYS); root.getChildren().addAll(modalPane, container); - NodeUtils.setAnchors(root, Insets.EMPTY); + FxUtil.setAnchors(root, Insets.EMPTY); // 场景 - scene = new Scene(root); + Scene scene = new Scene(root); stage.setTitle("Git批量管理工具"); stage.setMinWidth(1100); stage.setMinHeight(650); diff --git a/src/main/java/com/light/component/AddLocalProjectPane.java b/src/main/java/com/light/component/AddLocalProjectPane.java new file mode 100644 index 0000000000000000000000000000000000000000..f8021c4d8986177cd709aeede4c76dafe8262948 --- /dev/null +++ b/src/main/java/com/light/component/AddLocalProjectPane.java @@ -0,0 +1,95 @@ +package com.light.component; + +import com.light.enums.Level; +import com.light.util.NoticeUtils; +import javafx.geometry.Pos; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.layout.HBox; +import javafx.stage.DirectoryChooser; +import javafx.stage.Modality; +import javafx.stage.Stage; +import javafx.stage.Window; +import org.apache.commons.lang3.StringUtils; + +import java.io.File; + +public class AddLocalProjectPane extends Stage { + + private static final String INIT_DIR = "D:"; + private final Window window; + private final TextField textField = new TextField(); + private final Button load = new Button("加载"); + private final HBox inputHBox = new HBox(10); + + private final DirectoryChooser dirChooser = new DirectoryChooser(); + + private static AddLocalProjectPane instance; + + public AddLocalProjectPane(Window window) { + super(); + this.window = window; + + initialize(); + Scene scene = new Scene(inputHBox, 700, 100); + //设置此窗口在优先级最高 屏蔽其他窗口 + setScene(scene); + initModality(Modality.WINDOW_MODAL); + getIcons().add(new Image("/icons/git.png")); + setTitle("添加本地项目"); + initOwner(window); + initEvent(); + } + + public static void showPane(Window window) { + if (instance == null) { + instance = new AddLocalProjectPane(window); + } + instance.show(); + } + + private void initialize() { + //输入框横向布局 + inputHBox.setAlignment(Pos.CENTER); + //错误提示横向布局 + HBox noticeHBox = new HBox(10); + noticeHBox.setAlignment(Pos.CENTER); + + //输入框 + textField.setPromptText("请输入代码存放路径"); + textField.setMinWidth(550); + //设置打开窗口时焦点不在输入框中 + textField.setFocusTraversable(false); + //加载按钮 + inputHBox.getChildren().addAll(textField, load); + } + + private void initEvent() { + load.setOnAction(loadBtn -> { + System.out.println("用户输入路径:" + textField.getText()); + //错误提示 + try { + if (StringUtils.isEmpty(textField.getText())) { + NoticeUtils.show(window, "请输入路径", Level.WARN); + } else { + File file = new File(textField.getText()); + if (file.exists()) { + //todo 输入正确,读取文件夹中内容 + + } else { + NoticeUtils.show(getScene().getWindow(), "该路径不存在或路径下无内容", Level.DANGER); + } + } + } catch (Exception e) { + NoticeUtils.show(getScene().getWindow(), "路径不正确", Level.DANGER); + } + }); + + textField.setOnMouseClicked(event -> { + dirChooser.setInitialDirectory(new File(INIT_DIR)); + dirChooser.showDialog(window); + }); + } +} diff --git a/src/main/java/com/light/component/AuthenticationPane.java b/src/main/java/com/light/component/AuthenticationPane.java new file mode 100644 index 0000000000000000000000000000000000000000..31170934a31798adaa86eb2ae7076f7ce609e1c4 --- /dev/null +++ b/src/main/java/com/light/component/AuthenticationPane.java @@ -0,0 +1,61 @@ +package com.light.component; + +import com.light.enums.Level; +import com.light.layout.ModalDialog; +import com.light.model.GitAuthInfo; +import com.light.thread.AsyncTask; +import com.light.util.H2PoolUtils; +import com.light.util.JGitUtils; +import com.light.util.NoticeUtils; +import javafx.geometry.Insets; +import javafx.scene.control.Button; +import javafx.scene.control.PasswordField; +import javafx.scene.control.TextField; +import javafx.scene.layout.VBox; +import org.apache.commons.lang3.StringUtils; + +import java.io.File; + +public class AuthenticationPane extends ModalDialog { + + public AuthenticationPane(String repoType, String remoteUrl, File file) { + super(); + header.setTitle(repoType); + content.setBody(createContent(repoType, remoteUrl, file)); + content.setFooter(null); + } + + private VBox createContent(String repoType, String remoteUrl, File file) { + var usernameField = new TextField(); + usernameField.setPromptText("手机/邮箱/用户名"); + usernameField.setMaxWidth(300); + var passwordField = new PasswordField(); + passwordField.setPromptText("请输入密码"); + passwordField.setMaxWidth(300); + Button saveButton = new Button("确定"); + saveButton.setMaxWidth(300); + + var root = new VBox(10, usernameField, passwordField, saveButton); + root.setPadding(new Insets(20)); + + saveButton.setOnMouseClicked(event -> { + String username = usernameField.getText(); + String password = passwordField.getText(); + if (StringUtils.isAnyBlank(username, password)) { + NoticeUtils.show(null, "用户名密码不能为空", Level.WARN); + return; + } + GitAuthInfo authInfo = new GitAuthInfo(0, repoType, username, username, ""); + // 入库 + int num = H2PoolUtils.insertAuthInfo(authInfo); + if (num > 0) { + AsyncTask.runOnce("克隆项目", () -> { + JGitUtils.AUTH_INFO_MAP.put(repoType, authInfo); + JGitUtils.cloneRepo(remoteUrl, file, authInfo); + }); + } + }); + + return root; + } +} diff --git a/src/main/java/com/light/component/LevelTableCell.java b/src/main/java/com/light/component/LevelTableCell.java index cfe17dae98bbeee6162bb6ba581f0e02206b65fb..74cd218f6bcda0fcd27ceef23de602bc185f083b 100644 --- a/src/main/java/com/light/component/LevelTableCell.java +++ b/src/main/java/com/light/component/LevelTableCell.java @@ -1,7 +1,12 @@ package com.light.component; import com.light.model.GitProject; +import com.light.thread.AsyncTask; +import com.light.util.H2PoolUtils; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; import javafx.geometry.Pos; +import javafx.scene.control.ChoiceBox; import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.layout.HBox; @@ -16,6 +21,10 @@ public class LevelTableCell extends TableCell { private final HBox hBox = new HBox(); + private final ObservableList stars = FXCollections.observableArrayList(0, 1, 2, 3, 4, 5); + + private ChoiceBox choiceBox; + public static Callback, TableCell> forTableColumn() { return param -> new LevelTableCell(); } @@ -37,4 +46,44 @@ public class LevelTableCell extends TableCell { setGraphic(hBox); } } + + @Override + public void startEdit() { + super.startEdit(); + if (!isEditing()) { + return; + } + int oldValue = getItem(); + if (choiceBox == null) { + choiceBox = new ChoiceBox<>(stars); + choiceBox.setMaxWidth(Double.MAX_VALUE); + choiceBox.showingProperty().addListener(event -> { + if (!choiceBox.isShowing()) { + commitEdit(choiceBox.getValue()); + if (oldValue != choiceBox.getValue()) { + AsyncTask.runOnce("更新学习等级", () -> H2PoolUtils.updateGitProject(getTableRow().getItem())); + } + } + }); + } + + choiceBox.getSelectionModel().select(getItem()); + setText(null); + setGraphic(choiceBox); + } + + @Override + public void commitEdit(Integer newValue) { + super.commitEdit(newValue); + } + + @Override + public void cancelEdit() { + super.cancelEdit(); + hBox.getChildren().clear(); + for (int i = 0; i < getItem(); i++) { + hBox.getChildren().add(new FontIcon(AntDesignIconsOutlined.STAR)); + } + setGraphic(hBox); + } } diff --git a/src/main/java/com/light/component/OperationTableCell.java b/src/main/java/com/light/component/OperationTableCell.java index 2db4141d35ebf254700f60dcdf6feabd5fda44f5..7b5e7fa439be0cd23cc473e95175cc1aaa32ca5f 100644 --- a/src/main/java/com/light/component/OperationTableCell.java +++ b/src/main/java/com/light/component/OperationTableCell.java @@ -2,7 +2,7 @@ package com.light.component; import atlantafx.base.theme.Styles; import com.light.model.GitProject; -import com.light.util.FxDataUtil; +import com.light.util.FxApplicationContextUtils; import javafx.beans.property.SimpleDoubleProperty; import javafx.geometry.Pos; import javafx.scene.control.Button; @@ -10,18 +10,17 @@ import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.layout.HBox; import javafx.util.Callback; -import org.apache.commons.lang3.StringUtils; /** * 操作列 - 按钮 */ -public class OperationTableCell extends TableCell { +public class OperationTableCell extends TableCell { Button updateButton = new Button("更新"); Button detailButton = new Button("详情"); HBox operationBox = new HBox(updateButton, detailButton); private SimpleDoubleProperty rate; - public static Callback, TableCell> forTableColumn() { + public static Callback, TableCell> forTableColumn() { return param -> new OperationTableCell(); } @@ -35,9 +34,9 @@ public class OperationTableCell extends TableCell { } @Override - protected void updateItem(String item, boolean empty) { + protected void updateItem(Integer item, boolean empty) { super.updateItem(item, empty); - if (empty || getTableRow() == null || StringUtils.isBlank(item)) { + if (empty || getTableRow() == null || null == item) { setGraphic(null); } else { rate = null; @@ -53,7 +52,7 @@ public class OperationTableCell extends TableCell { // TODO Auto-generated method stub updateButton.setOnMouseClicked(event -> { rate.set(0.0); - FxDataUtil.UPDATE_PROPERTY.set(FxDataUtil.UPDATE_NUMBER.incrementAndGet()); + FxApplicationContextUtils.UPDATE_PROPERTY.set(FxApplicationContextUtils.UPDATE_NUMBER.incrementAndGet() + ""); for (int i = 0; i <= 100; i++) { double dRate = (double) i / 100; rate.set(dRate); diff --git a/src/main/java/com/light/component/TooltipTableRow.java b/src/main/java/com/light/component/TooltipTableRow.java index a98d7ec90e957fd2e68986b755f25d694b7f0f5a..c7e4a9939ee7a6505b982d12e0777d20bf96bbaf 100644 --- a/src/main/java/com/light/component/TooltipTableRow.java +++ b/src/main/java/com/light/component/TooltipTableRow.java @@ -1,7 +1,6 @@ package com.light.component; import atlantafx.base.controls.Card; -import atlantafx.base.controls.Tile; import atlantafx.base.theme.Styles; import com.light.model.GitProject; import javafx.geometry.Orientation; @@ -20,7 +19,9 @@ public class TooltipTableRow extends TableRow { private final Tooltip tooltip = new Tooltip(); - private final Tile name = new Tile(); + private final Text name = new Text(); + private final Text description = new Text(); + private final Text author = new Text(); private final Label remoteAddr = new Label(); @@ -39,8 +40,10 @@ public class TooltipTableRow extends TableRow { tooltip.getStyleClass().removeAll("tooltip"); toolTipCard.getStyleClass().add(Styles.ELEVATED_1); toolTipCard.setMinWidth(300); - toolTipCard.setMaxWidth(300); - toolTipCard.setHeader(name); + toolTipCard.setMaxWidth(700); + name.getStyleClass().add(Styles.TITLE_3); + VBox header = new VBox(5, name, description); + toolTipCard.setHeader(header); // 分割线 Separator separator = new Separator(Orientation.HORIZONTAL); @@ -64,16 +67,16 @@ public class TooltipTableRow extends TableRow { private void refreshData(GitProject item) { levelBox.getChildren().clear(); // 项目名称 - name.setTitle(item.name().get()); - name.setDescription(item.description().get()); - author.setText("仓库 作者: " + item.author()); + name.setText(item.name().get()); + description.setText(item.description().get()); + author.setText("仓库 作者: " + item.author().get()); remoteAddr.setText("仓库 地址: " + item.remote()); - localAddr.setText("本地 地址: " + item.local()); + localAddr.setText("本地 地址: " + item.local().get()); level.setText("学习 等级: "); levelBox.getChildren().add(level); for (int i = 0; i < item.level().get(); i++) { levelBox.getChildren().add(new FontIcon(AntDesignIconsOutlined.STAR)); } - remark.setText("仓库 备注: " + item.remark()); + remark.setText("仓库 备注: " + item.remark().get()); } } diff --git a/src/main/java/com/light/layout/MenuPane.java b/src/main/java/com/light/layout/MenuPane.java index ee74d114d60519b8372f325004e132899c41a523..512fb33152ced9c9d44fd8a6a6566cbc0ac9c2f0 100644 --- a/src/main/java/com/light/layout/MenuPane.java +++ b/src/main/java/com/light/layout/MenuPane.java @@ -2,7 +2,7 @@ package com.light.layout; import atlantafx.base.theme.Styles; import com.light.theme.ThemeDialog; -import com.light.util.FxDataUtil; +import com.light.util.FxApplicationContextUtils; import com.light.util.FxUtil; import com.light.util.Lazy; import com.light.view.HomeView; @@ -39,10 +39,10 @@ public class MenuPane extends StackPane { // 底部导航栏 private final Label downloadLabel = new Label("正在下载", new FontIcon(BootstrapIcons.ARROW_DOWN)); - private final Text downloadLabelText = new Text(FxDataUtil.DOWNLOAD_PROPERTY.getValue().toString()); + private final Text downloadLabelText = new Text(FxApplicationContextUtils.DOWNLOAD_PROPERTY.getValue().toString()); private final HBox downloadBox = new HBox(10, downloadLabel, downloadLabelText); private final Label updateLabel = new Label("正在更新", new FontIcon(BootstrapIcons.ARROW_DOWN)); - private final Text updateLabelText = new Text(FxDataUtil.UPDATE_PROPERTY.getValue().toString()); + private final Text updateLabelText = new Text(FxApplicationContextUtils.UPDATE_PROPERTY.getValue().toString()); private final HBox updateBox = new HBox(10, updateLabel, updateLabelText); private final VBox bottomMenu = new VBox(downloadBox, updateBox); @@ -99,12 +99,8 @@ public class MenuPane extends StackPane { }); }); - FxDataUtil.DOWNLOAD_PROPERTY.addListener((observable, oldValue, newValue) -> { - downloadLabelText.setText(newValue.toString()); - }); - FxDataUtil.UPDATE_PROPERTY.addListener((observable, oldValue, newValue) -> { - updateLabelText.setText(newValue.toString()); - }); + downloadLabelText.textProperty().bindBidirectional(FxApplicationContextUtils.DOWNLOAD_PROPERTY); + updateLabelText.textProperty().bindBidirectional(FxApplicationContextUtils.UPDATE_PROPERTY); // 设置 setting.setOnMouseClicked(event -> { diff --git a/src/main/java/com/light/layout/ModalDialog.java b/src/main/java/com/light/layout/ModalDialog.java index c3d2994eb1ea17740f70e5fc6adbbe20147b871c..0e87908efb07bc963edf32adaad18a7c86236c41 100644 --- a/src/main/java/com/light/layout/ModalDialog.java +++ b/src/main/java/com/light/layout/ModalDialog.java @@ -16,7 +16,7 @@ import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; -import static com.light.util.NodeUtils.MAIN_MODAL_ID; +import static com.light.util.FxUtil.MAIN_MODAL_ID; /** * 遮罩层对话框 diff --git a/src/main/java/com/light/model/GitAuthInfo.java b/src/main/java/com/light/model/GitAuthInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..191bc7f5b6d6fe6b6fa662e9e159dc55feebb47e --- /dev/null +++ b/src/main/java/com/light/model/GitAuthInfo.java @@ -0,0 +1,4 @@ +package com.light.model; + +public record GitAuthInfo (int id, String type, String username, String password, String createTime){ +} diff --git a/src/main/java/com/light/model/GitProject.java b/src/main/java/com/light/model/GitProject.java index 4d7a557ebbd1a91bb820fc103c9e141917dcb421..a5af28b4653a87a67f6c0d5c3ba48e9d55109288 100644 --- a/src/main/java/com/light/model/GitProject.java +++ b/src/main/java/com/light/model/GitProject.java @@ -1,12 +1,12 @@ package com.light.model; -import com.light.util.FxDataUtil; +import com.light.util.FxApplicationContextUtils; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleDoubleProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleStringProperty; -public record GitProject(SimpleStringProperty id, +public record GitProject(SimpleIntegerProperty id, SimpleStringProperty name, SimpleStringProperty author, SimpleStringProperty branch, @@ -24,7 +24,7 @@ public record GitProject(SimpleStringProperty id, downloadRate.addListener((observable, oldValue, newValue) -> { if (newValue != null && newValue.doubleValue() >= 1.0) { selected.set(false); - FxDataUtil.UPDATE_PROPERTY.set(FxDataUtil.UPDATE_NUMBER.decrementAndGet()); + FxApplicationContextUtils.UPDATE_PROPERTY.set(FxApplicationContextUtils.UPDATE_NUMBER.decrementAndGet() + ""); } }); } diff --git a/src/main/java/com/light/theme/ThemeDialog.java b/src/main/java/com/light/theme/ThemeDialog.java index 8128403d2ed5fd2240755efb84e4390815dab73b..8058c7648f3c1d7b208b2277df050aaf5e6855d8 100644 --- a/src/main/java/com/light/theme/ThemeDialog.java +++ b/src/main/java/com/light/theme/ThemeDialog.java @@ -5,7 +5,7 @@ package com.light.theme; import atlantafx.base.theme.Theme; import com.light.layout.ModalDialog; import com.light.thread.AsyncTask; -import com.light.util.FxDataUtil; +import com.light.util.FxApplicationContextUtils; import com.light.util.H2PoolUtils; import javafx.application.Application; import javafx.geometry.Insets; @@ -16,7 +16,7 @@ import javafx.scene.layout.VBox; import java.util.Objects; -import static com.light.util.FxDataUtil.THEME_LIST; +import static com.light.util.FxApplicationContextUtils.THEME_LIST; /** * 主题对话框 @@ -37,14 +37,9 @@ public final class ThemeDialog extends ModalDialog { thumbnailsGroup.selectedToggleProperty().addListener((obs, old, val) -> { if (val != null && val.getUserData() instanceof Theme theme) { - FxDataUtil.CURRENT_THEME_NAME.set(theme.getName()); + FxApplicationContextUtils.CURRENT_THEME_NAME.set(theme.getName()); Application.setUserAgentStylesheet(theme.getUserAgentStylesheet()); - AsyncTask.runOnce(new AsyncTask("","主题更新成功") { - @Override - protected void handler() throws Exception { - H2PoolUtils.updateDictData("GIT_CURRENT_THEME", theme.getName()); - } - }); + AsyncTask.runOnce("主题更新", () -> H2PoolUtils.updateDictData("GIT_CURRENT_THEME", theme.getName())); } }); } @@ -65,7 +60,7 @@ public final class ThemeDialog extends ModalDialog { thumbnail.setToggleGroup(thumbnailsGroup); thumbnail.setUserData(theme); thumbnail.setSelected(Objects.equals( - FxDataUtil.CURRENT_THEME_NAME.get(), + FxApplicationContextUtils.CURRENT_THEME_NAME.get(), theme.getName() )); thumbnailsPane.getChildren().add(thumbnail); diff --git a/src/main/java/com/light/thread/AsyncEvent.java b/src/main/java/com/light/thread/AsyncEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..0ec782afbc59c93e31c44bd7c5f001ee6ed3df41 --- /dev/null +++ b/src/main/java/com/light/thread/AsyncEvent.java @@ -0,0 +1,6 @@ +package com.light.thread; + +public interface AsyncEvent { + + void handler() throws Exception; +} diff --git a/src/main/java/com/light/thread/AsyncTask.java b/src/main/java/com/light/thread/AsyncTask.java index 93198d88957817fd21c8726a09453627ba904a23..8fff8da66b0e2211f707cb69635e31a3b966b1a4 100644 --- a/src/main/java/com/light/thread/AsyncTask.java +++ b/src/main/java/com/light/thread/AsyncTask.java @@ -4,36 +4,29 @@ import javafx.concurrent.Task; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public abstract class AsyncTask extends Task { +public class AsyncTask extends Task { public static final Logger LOGGER = LoggerFactory.getLogger(AsyncTask.class); - /** - * 线程名称 - */ - private final String threadName; - private final String description; private final long startTime; - public AsyncTask() { - this(null, null); - } + private final AsyncEvent asyncEvent; - public AsyncTask(String threadName, String description) { - this.threadName = threadName; + public AsyncTask(String description, AsyncEvent asyncEvent) { this.description = description; + this.asyncEvent = asyncEvent; this.startTime = System.currentTimeMillis(); } @Override protected Void call() throws Exception { try { - LOGGER.info("Task {} 开始执行 - {}", threadName, description); - handler(); + LOGGER.info("Task {} 开始执行", description); + asyncEvent.handler(); } catch (Exception e) { - LOGGER.info("Task {} 执行异常:{}", threadName, e.getMessage()); + LOGGER.info("Task {} 执行异常:{}", description, e.getMessage()); } return null; } @@ -41,22 +34,20 @@ public abstract class AsyncTask extends Task { @Override protected void succeeded() { super.succeeded(); - LOGGER.info("Task {} 执行结束,耗时 {} ms", threadName, System.currentTimeMillis() - startTime); + LOGGER.info("Task {} 执行结束,耗时 {} ms", description, System.currentTimeMillis() - startTime); } @Override protected void failed() { super.failed(); - LOGGER.info("Task {} 执行失败", threadName); + LOGGER.info("Task {} 执行失败", description); } - protected abstract void handler() throws Exception; - - public static void runOnce(AsyncTask asyncTask) { - GitThreadPool.EXECUTOR_SERVICE.execute(asyncTask); + public static void runOnce(AsyncEvent event) { + runOnce("任务", event); } - public String getThreadName() { - return threadName; + public static void runOnce(String description, AsyncEvent event) { + GitThreadPool.EXECUTOR_SERVICE.execute(new AsyncTask(description, event)); } } diff --git a/src/main/java/com/light/util/FxDataUtil.java b/src/main/java/com/light/util/FxApplicationContextUtils.java similarity index 78% rename from src/main/java/com/light/util/FxDataUtil.java rename to src/main/java/com/light/util/FxApplicationContextUtils.java index 7fb351e66c13352774ae7ccc2d73f248cfd172f4..7afdffade5335feb48fe02d9945e0bcbe55bf798 100644 --- a/src/main/java/com/light/util/FxDataUtil.java +++ b/src/main/java/com/light/util/FxApplicationContextUtils.java @@ -2,7 +2,6 @@ package com.light.util; import atlantafx.base.theme.*; import com.light.model.GitProject; -import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleStringProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; @@ -14,18 +13,18 @@ import java.util.concurrent.atomic.AtomicInteger; /** * 全局数据工具类 */ -public final class FxDataUtil { +public final class FxApplicationContextUtils { /** * 正在下载数量 */ public static final AtomicInteger DOWNLOAD_NUMBER = new AtomicInteger(0); - public static final SimpleIntegerProperty DOWNLOAD_PROPERTY = new SimpleIntegerProperty(DOWNLOAD_NUMBER.intValue()); + public static final SimpleStringProperty DOWNLOAD_PROPERTY = new SimpleStringProperty(DOWNLOAD_NUMBER.toString()); /** * 正在更新数量 */ public static final AtomicInteger UPDATE_NUMBER = new AtomicInteger(0); - public static final SimpleIntegerProperty UPDATE_PROPERTY = new SimpleIntegerProperty(UPDATE_NUMBER.intValue()); + public static final SimpleStringProperty UPDATE_PROPERTY = new SimpleStringProperty(UPDATE_NUMBER.toString()); /** * git项目集合 diff --git a/src/main/java/com/light/util/FxUtil.java b/src/main/java/com/light/util/FxUtil.java index 853701b04fda741d4ea8db8075bb8fb46b9e48a3..999b9c298e11e332aca48358af618086ae740192 100644 --- a/src/main/java/com/light/util/FxUtil.java +++ b/src/main/java/com/light/util/FxUtil.java @@ -4,9 +4,11 @@ import javafx.beans.binding.Bindings; import javafx.beans.property.DoubleProperty; import javafx.beans.property.SimpleDoubleProperty; import javafx.geometry.Bounds; +import javafx.geometry.Insets; import javafx.scene.Node; import javafx.scene.image.Image; import javafx.scene.image.ImageView; +import javafx.scene.layout.AnchorPane; import javafx.scene.layout.Region; import javafx.scene.shape.Rectangle; import javafx.stage.Stage; @@ -22,6 +24,7 @@ public class FxUtil { /** * 获取资源文件 + * * @param resources * @return */ @@ -31,6 +34,7 @@ public class FxUtil { /** * 获取图片 + * * @param resources * @return */ @@ -136,4 +140,26 @@ public class FxUtil { return rectangle; } + public static final String MAIN_MODAL_ID = "modal-pane"; + + /** + * 设置子节点距AnchorPane锚点布局四边的距离 + * + * @param node + * @param insets + */ + public static void setAnchors(Node node, Insets insets) { + if (insets.getTop() >= 0) { + AnchorPane.setTopAnchor(node, insets.getTop()); + } + if (insets.getRight() >= 0) { + AnchorPane.setRightAnchor(node, insets.getRight()); + } + if (insets.getBottom() >= 0) { + AnchorPane.setBottomAnchor(node, insets.getBottom()); + } + if (insets.getLeft() >= 0) { + AnchorPane.setLeftAnchor(node, insets.getLeft()); + } + } } diff --git a/src/main/java/com/light/util/H2PoolUtils.java b/src/main/java/com/light/util/H2PoolUtils.java index 1dd4e2e8f91da469c05273ccb44ccea9b091e0d0..2ee47914d1502a321601e4dd66a8bd08f4d93739 100644 --- a/src/main/java/com/light/util/H2PoolUtils.java +++ b/src/main/java/com/light/util/H2PoolUtils.java @@ -1,5 +1,6 @@ package com.light.util; +import com.light.model.GitAuthInfo; import com.light.model.GitNotice; import com.light.model.GitProject; import javafx.beans.property.SimpleBooleanProperty; @@ -143,7 +144,103 @@ public class H2PoolUtils { ) """; stmt.executeUpdate(createGitProjectNoticeHistoryTable); - LOGGER.info("h2 通知消息历史表创建成功"); + + // 初始化仓库权限表 + String createGitAuthenticationTable = """ + create table git_auth_info + ( + id int primary key auto_increment, + type varchar(10) comment '仓库类型', + username varchar(50) comment '用户名', + password varchar(50) comment '密码', + createtime varchar(20) comment '创建时间' + ) + """; + stmt.executeUpdate(createGitAuthenticationTable); + LOGGER.info("h2 仓库权限表创建成功"); + } + + public static GitAuthInfo queryAuthInfo(String type) { + try (Connection conn = getConnection()) { + String sql = "select * from git_auth_info where type = ?"; + PreparedStatement statement = conn.prepareStatement(sql); + statement.setString(1, type); + ResultSet resultSet = statement.executeQuery(); + while (resultSet.next()) { + return new GitAuthInfo( + resultSet.getInt("id"), + resultSet.getString("type"), + resultSet.getString("username"), + resultSet.getString("password"), + resultSet.getString("createtime") + ); + } + } catch (SQLException e) { + LOGGER.error("通过 {} 查询权限信息异常:{}", type, e.getMessage()); + } + return null; + } + + public static List queryAllAuthInfo() { + List list = new ArrayList<>(); + try (Connection conn = getConnection()) { + String sql = "select * from git_auth_info"; + PreparedStatement statement = conn.prepareStatement(sql); + ResultSet resultSet = statement.executeQuery(); + while (resultSet.next()) { + list.add(new GitAuthInfo( + resultSet.getInt("id"), + resultSet.getString("type"), + resultSet.getString("username"), + resultSet.getString("password"), + resultSet.getString("createtime") + )); + } + } catch (SQLException e) { + LOGGER.error("获取所有权限信息异常:{}", e.getMessage()); + } + return list; + } + + public static int insertAuthInfo(GitAuthInfo authInfo) { + try (Connection conn = getConnection()) { + String sql = "INSERT INTO git_auth_info(type, username, password, createtime) values(?,?,?,?)"; + PreparedStatement statement = conn.prepareStatement(sql); + statement.setString(1, authInfo.type()); + statement.setString(2, authInfo.username()); + statement.setString(3, authInfo.password()); + statement.setString(4, DateUtils.formatDateTime(new Date())); + return statement.executeUpdate(); + } catch (SQLException e) { + LOGGER.error("记录权限信息异常:{}", e.getMessage()); + } + return 0; + } + + public static int updateAuthInfo(String type, String username, String password) { + try (Connection conn = getConnection()) { + String sql = "update git_auth_info set username = ?, password = ? where type = ?"; + PreparedStatement statement = conn.prepareStatement(sql); + statement.setString(1, username); + statement.setString(2, password); + statement.setString(3, type); + return statement.executeUpdate(); + } catch (SQLException e) { + LOGGER.error("通过 {} 更新权限信息为 {} 失败:{}", type, username, e.getMessage()); + } + return 0; + } + + public static int deleteAuthInfo(String type) { + try (Connection conn = getConnection()) { + String sql = "delete from git_auth_info where type = ?"; + PreparedStatement statement = conn.prepareStatement(sql); + statement.setString(1, type); + return statement.executeUpdate(); + } catch (SQLException e) { + LOGGER.error("通过 {} 删除权限信息失败:{}", type, e.getMessage()); + } + return 0; } /** @@ -167,7 +264,7 @@ public class H2PoolUtils { public static String queryDictByLabel(String label, String defaultValue) { String value = defaultValue; - try (Connection conn = getConnection();) { + try (Connection conn = getConnection()) { String sql = "select * from git_project_dict where label = ?"; PreparedStatement statement = conn.prepareStatement(sql); statement.setString(1, label); @@ -252,10 +349,11 @@ public class H2PoolUtils { statement.setString(3, project.branch().get()); statement.setString(4, project.remote()); statement.setString(5, project.local().get()); - Date date = new Date(); - statement.setString(6, DateUtils.formatDateTime(date)); - statement.setString(7, DateUtils.formatDateTime(date)); + statement.setString(6, project.createTime()); + statement.setString(7, project.updateTime().get()); statement.executeUpdate(); + + existsGitProjects(project); } catch (SQLException e) { LOGGER.error("项目信息保存异常:{}", e.getMessage()); } @@ -271,7 +369,7 @@ public class H2PoolUtils { ResultSet resultSet = statement.getResultSet(); while (resultSet.next()) { GitProject gitProject = new GitProject( - new SimpleStringProperty(resultSet.getInt("id") + ""), + new SimpleIntegerProperty(resultSet.getInt("id")), new SimpleStringProperty(resultSet.getString("name")), new SimpleStringProperty(resultSet.getString("author")), new SimpleStringProperty(resultSet.getString("branch")), @@ -286,13 +384,47 @@ public class H2PoolUtils { new SimpleBooleanProperty(false) ); gitProject.addSelectedListener(); - FxDataUtil.GIT_PROJECT_OBSERVABLE_LIST.add(gitProject); + FxApplicationContextUtils.GIT_PROJECT_OBSERVABLE_LIST.add(gitProject); } } catch (SQLException e) { LOGGER.error("获取所有项目信息异常:{}", e.getMessage()); } } + public static boolean existsGitProjects(GitProject project) { + try (Connection conn = getConnection()) { + String sql = "select id from git_project_info where name = ? and author = ?"; + PreparedStatement statement = conn.prepareStatement(sql); + statement.setString(1, project.name().get()); + statement.setString(2, project.author().get()); + ResultSet resultSet = statement.executeQuery(); + + while (resultSet.next()) { + project.id().set(resultSet.getInt("id")); + return true; + } + } catch (SQLException e) { + LOGGER.error("根据 {}、{} 获取项目信息异常:{}", project.name().get(), project.author().get(), e.getMessage()); + } + return false; + } + + public static boolean existsGitProjects(String name, String author) { + try (Connection conn = getConnection()) { + String sql = "select id from git_project_info where name = ? and author = ?"; + PreparedStatement statement = conn.prepareStatement(sql); + statement.setString(1, name); + statement.setString(2, author); + ResultSet resultSet = statement.executeQuery(); + while (resultSet.next()) { + return true; + } + } catch (SQLException e) { + LOGGER.error("根据 {}、{} 获取项目信息异常:{}", name, author, e.getMessage()); + } + return false; + } + public static void updateGitProject(GitProject project) { try (Connection conn = getConnection()) { String sql = "update git_project_info set branch = ? ,updatetime = ?, local = ?, description = ?, remark = ?, level = ? where id = ?"; @@ -303,7 +435,7 @@ public class H2PoolUtils { statement.setString(4, project.description().get()); statement.setString(5, project.remark().get()); statement.setInt(6, project.level().get()); - statement.setInt(7, Integer.parseInt(project.id().get())); + statement.setInt(7, project.id().get()); statement.executeUpdate(); } catch (SQLException e) { LOGGER.error("更新项目信息异常:{}", e.getMessage()); diff --git a/src/main/java/com/light/util/JGitUtils.java b/src/main/java/com/light/util/JGitUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..10fdc1456953c28367494b3ba1f6914aef0cee72 --- /dev/null +++ b/src/main/java/com/light/util/JGitUtils.java @@ -0,0 +1,240 @@ +package com.light.util; + +import com.light.model.GitAuthInfo; +import com.light.model.GitProject; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleDoubleProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.beans.property.SimpleStringProperty; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.transport.CredentialsProvider; +import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +public class JGitUtils { + public static final Logger LOGGER = LoggerFactory.getLogger(JGitUtils.class); + + // 存放gitee、GitHub用户名密码 + public static final Map AUTH_INFO_MAP = new HashMap<>(); + + // 存放解析出来的本地项目 + private static final List PROJECT_FILE = new ArrayList<>(); + + /** + * 凭证管理 + * + * @param userName + * @param password + * @return + */ + public static CredentialsProvider createCredential(String userName, String password) { + return new UsernamePasswordCredentialsProvider(userName, password); + } + + public static GitAuthInfo isExistsAuthInfo(String remoteUrl) { + String type = getType(remoteUrl); + if ("other".equals(type)) { + return new GitAuthInfo(0, "other", "test", "test", ""); + } else { + GitAuthInfo authInfo = AUTH_INFO_MAP.get(type); + if (authInfo == null) { + authInfo = H2PoolUtils.queryAuthInfo(type); + AUTH_INFO_MAP.put(type, authInfo); + } + return authInfo; + } + } + + public static String getType(String remoteUrl) { + if (remoteUrl.contains("github")) { + return "Github"; + } else if (remoteUrl.contains("gitee")) { + return "Gitee"; + } + return "other"; + } + + /** + * 获取项目名称 + * + * @param remoteUrl + * @return + */ + public static String getRepoName(String remoteUrl) { + String name = ""; + if (remoteUrl.endsWith(".git") || remoteUrl.endsWith(".gi") || remoteUrl.endsWith(".g") || remoteUrl.endsWith(".")) { + name = remoteUrl.substring(remoteUrl.lastIndexOf("/") + 1, remoteUrl.lastIndexOf(".")); + } else { + name = remoteUrl.substring(remoteUrl.lastIndexOf("/") + 1); + } + return name; + } + + /** + * 获取项目作者 + * + * @param remoteUrl + * @return + */ + public static String getAuthor(String remoteUrl) { + String author = remoteUrl.substring(0, remoteUrl.lastIndexOf("/")); + return author = author.substring(author.lastIndexOf("/") + 1); + } + + /** + * 解析指定目录下所有符合git要求的项目地址 + * + * @param repo + */ + public static void parseLocalProjectPath(File repo) { + if (Objects.nonNull(repo) && repo.isDirectory()) { + String absolutePath = repo.getAbsolutePath(); + String gitDir = absolutePath.substring(absolutePath.lastIndexOf(File.separator) + 1); + switch (gitDir) { + case ".git" -> PROJECT_FILE.add(repo); + case ".idea", "src", ".vscode", "target", "out", ".gradle", "gradle", "build", "log", "logs", "system", ".vs", "obj", "bin", "Assets", ".gitea", + ".github", "tests", "doc", "tools", "scripts", "resources" -> { + } + default -> { + File[] files = repo.listFiles(); + if (null != files) { + // 过滤目录下的所有文件,如果和.git匹配上了,说明这个项目是git项目,则后续不需要继续比对 + boolean exists = Arrays.stream(files).anyMatch(file -> { + String subPath = file.getAbsolutePath(); + String subGitDir = subPath.substring(subPath.lastIndexOf(File.separator) + 1); + return ".git".equals(subGitDir); + }); + if (!exists) { + // 如果不存在匹配上的.git,则继续往下遍历查找 + Arrays.stream(files).parallel().forEach(JGitUtils::parseLocalProjectPath); + } else { + Arrays.stream(files).filter(file -> { + String subPath = file.getAbsolutePath(); + String subGitDir = subPath.substring(subPath.lastIndexOf(File.separator) + 1); + return ".git".equals(subGitDir); + }).findFirst().ifPresent(PROJECT_FILE::add); + } + } + } + } + } + } + + /** + * 需要在方法 parseLocalProjectPath 之后执行,解析所有符合要求的项目并入库 + */ + public static void parseLocalProject() { + PROJECT_FILE.forEach(path -> { + try (Git git = Git.open(path)) { + String remotePath = git.remoteList().call().get(0).getURIs().get(0).toString(); + String name = remotePath.substring(remotePath.lastIndexOf("/") + 1, remotePath.lastIndexOf(".")); + String author = remotePath.substring(0, remotePath.lastIndexOf("/")); + String branch = git.getRepository().getBranch(); + String absolutePath = path.getAbsolutePath(); + String local = absolutePath.substring(0, absolutePath.lastIndexOf(".") - 1); + + GitProject newProject = new GitProject(new SimpleIntegerProperty(0), + new SimpleStringProperty(name), + new SimpleStringProperty(author.substring(author.lastIndexOf("/") + 1)), + new SimpleStringProperty(branch), + DateUtils.formatDateTime(new Date()), + new SimpleStringProperty(DateUtils.formatDateTime(new Date())), + remotePath, + new SimpleStringProperty(local), + new SimpleStringProperty(), + new SimpleStringProperty(), + new SimpleIntegerProperty(0), + new SimpleDoubleProperty(0.0), + new SimpleBooleanProperty(false) + ); + // 插入之前验证是否已经存在 + if (H2PoolUtils.existsGitProjects(newProject)) { + LOGGER.warn("项目 {},作者 {} 已经存在,不再新增", name, author); + newProject = null; + } else { + // 不存在则继续新增,并设置id + newProject.addSelectedListener(); + H2PoolUtils.insertProjectInfo(newProject); + FxApplicationContextUtils.GIT_PROJECT_OBSERVABLE_LIST.add(newProject); + } + } catch (Exception e) { + LOGGER.error("本地项目 {} 解析失败:{}", path.getAbsolutePath(), e.getMessage()); + } + }); + + // 清空缓存 + PROJECT_FILE.clear(); + } + + public static void cloneRepo(String remoteUrl, File localRepoFile, GitAuthInfo authInfo) { + // 更新下载数量 + FxApplicationContextUtils.DOWNLOAD_PROPERTY.set(String.valueOf(FxApplicationContextUtils.DOWNLOAD_NUMBER.incrementAndGet())); + + String name = getRepoName(remoteUrl); + String author = getAuthor(remoteUrl); + String local = localRepoFile.getAbsolutePath(); + + try (Git git = Git.cloneRepository() + .setCredentialsProvider(createCredential(authInfo.username(), authInfo.password())) + .setURI(remoteUrl).setDirectory(localRepoFile).call()) { + String branch = git.getRepository().getBranch(); + GitProject newProject = new GitProject(new SimpleIntegerProperty(0), + new SimpleStringProperty(name), + new SimpleStringProperty(author), + new SimpleStringProperty(branch), + DateUtils.formatDateTime(new Date()), + new SimpleStringProperty(DateUtils.formatDateTime(new Date())), + remoteUrl, + new SimpleStringProperty(local), + new SimpleStringProperty(), + new SimpleStringProperty(), + new SimpleIntegerProperty(0), + new SimpleDoubleProperty(0.0), + new SimpleBooleanProperty(false) + ); + newProject.addSelectedListener(); + H2PoolUtils.insertProjectInfo(newProject); + FxApplicationContextUtils.GIT_PROJECT_OBSERVABLE_LIST.add(newProject); + } catch (Exception e) { + LOGGER.error("项目 {},作者 {} 克隆失败:{}", name, author, e.getMessage()); + } + FxApplicationContextUtils.DOWNLOAD_PROPERTY.set(String.valueOf(FxApplicationContextUtils.DOWNLOAD_NUMBER.decrementAndGet())); + } + + /*private static void pull(String localDirPath, String remoteUrl) { + Git git = null; + try { + System.out.println("remoteUrl: " + remoteUrl + " --- localPath: " + localDirPath + " --- 开始拉取数据"); + File localPath = new File(localDirPath); + git = Git.open(localPath); + PullCommand pullCommand = git.pull(); + pullCommand.setTimeout(5); + pullCommand.setCredentialsProvider(usernamePasswordCredentialsProvider); + PullResult call = pullCommand.call(); + if (call.isSuccessful()) { + System.out.println("remoteUrl: " + remoteUrl + " --- localPath: " + localDirPath + " --- 拉取成功 --- 合并数据: " + call.getMergeResult()); + } + } catch (IOException e) { + githubMaxNum++; + System.out.println("IO pull : " + e.getMessage()); + } catch (GitAPIException e) { + githubMaxNum++; + System.out.println("Git pull : " + e.getMessage()); + } finally { + if (Objects.nonNull(git)) { + git.close(); + } + } + }*/ + + private static String getRemoteUrl(File localPath) throws IOException, GitAPIException { + Git git = Git.open(localPath); + return git.remoteList().call().get(0).getURIs().get(0).toString(); + } +} diff --git a/src/main/java/com/light/util/NodeUtils.java b/src/main/java/com/light/util/NodeUtils.java deleted file mode 100644 index e30d73692b80d28dd84c5cb00dd82c4792afd0b7..0000000000000000000000000000000000000000 --- a/src/main/java/com/light/util/NodeUtils.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.light.util; - -import javafx.geometry.Insets; -import javafx.scene.Node; -import javafx.scene.layout.AnchorPane; - -public final class NodeUtils { - public static final String MAIN_MODAL_ID = "modal-pane"; - - /** - * 设置子节点距AnchorPane锚点布局四边的距离 - * - * @param node - * @param insets - */ - public static void setAnchors(Node node, Insets insets) { - if (insets.getTop() >= 0) { - AnchorPane.setTopAnchor(node, insets.getTop()); - } - if (insets.getRight() >= 0) { - AnchorPane.setRightAnchor(node, insets.getRight()); - } - if (insets.getBottom() >= 0) { - AnchorPane.setBottomAnchor(node, insets.getBottom()); - } - if (insets.getLeft() >= 0) { - AnchorPane.setLeftAnchor(node, insets.getLeft()); - } - } -} diff --git a/src/main/java/com/light/view/HomeView.java b/src/main/java/com/light/view/HomeView.java index c57ba3af23ebdea7e500aed33a2665cf024a0a85..8ac4400f68783e741bfdb0a96d8249b5a76b21f4 100644 --- a/src/main/java/com/light/view/HomeView.java +++ b/src/main/java/com/light/view/HomeView.java @@ -1,65 +1,120 @@ package com.light.view; +import atlantafx.base.controls.CustomTextField; +import com.light.component.AuthenticationPane; import com.light.enums.Level; -import javafx.collections.FXCollections; +import com.light.model.GitAuthInfo; +import com.light.thread.AsyncTask; +import com.light.util.H2PoolUtils; +import com.light.util.JGitUtils; +import javafx.beans.property.SimpleStringProperty; import javafx.geometry.Pos; import javafx.scene.control.Button; -import javafx.scene.control.ChoiceBox; import javafx.scene.control.TextField; import javafx.scene.layout.BorderPane; import javafx.scene.layout.VBox; +import javafx.stage.DirectoryChooser; import org.apache.commons.lang3.StringUtils; +import org.kordamp.ikonli.antdesignicons.AntDesignIconsOutlined; +import org.kordamp.ikonli.javafx.FontIcon; + +import java.io.File; import static com.light.util.NoticeUtils.show; +import static com.light.view.ManagerView.INIT_DIR; public class HomeView extends BorderPane { - //下载新项目 两个输入框 克隆/下载地址 下载到哪里 一个分支复选框 一个下载按钮 - //添加本地项目 展示一个输入框 让用户键入代码存放路径 + private final DirectoryChooser dirChooser = new DirectoryChooser(); + private final SimpleStringProperty targetFolderPath = new SimpleStringProperty(); + private final TextField downloadPath = new TextField(); + private final CustomTextField targetPath = new CustomTextField(targetFolderPath.get()); + private final FontIcon right = new FontIcon(AntDesignIconsOutlined.FOLDER); + private final Button downloadButton = new Button("下载"); + public HomeView() { - /** - * 下载新项目 - * 输入框 下载地址 - * 输入框 目的地 - * 复选框 分支 - * 按钮 下载 - */ - //纵向布局 + // 纵向布局 VBox downloadVBox = new VBox(10); downloadVBox.setAlignment(Pos.CENTER); - //下载框 - var downloadPath = new TextField(); - downloadPath.setPromptText("请输入下载地址"); + // 下载框 + downloadPath.setPromptText("请输入远程项目地址"); downloadPath.setMaxWidth(600); - //目的地框 - var targetPath = new TextField(); - targetPath.setPromptText("请输入目的地"); + // 目的地框 + targetPath.setPromptText("请输入本地仓库地址"); + targetPath.setRight(right); targetPath.setMaxWidth(600); - //分支复选框 - ChoiceBox choiceBox = new ChoiceBox<>(FXCollections.observableArrayList("master", "dev", "A", "B", "C")); - choiceBox.setMaxWidth(600); - choiceBox.setValue("master"); - choiceBox.setOnAction(e -> { - System.out.println(choiceBox.getSelectionModel().getSelectedItem()); - }); - //下载按钮 - Button downloadButton = new Button("下载"); + // 下载按钮 + downloadButton.setDisable(true); + downloadVBox.getChildren().addAll(downloadPath, targetPath, downloadButton); + this.setCenter(downloadVBox); + + initEvent(); + } + + private void initEvent() { downloadButton.setOnAction(btn -> { - System.out.println("用户下载路径:" + downloadPath.getText()); - System.out.println("用户目标路径:" + targetPath.getText()); - //错误提示 try { - if (StringUtils.isEmpty(downloadPath.getText()) || StringUtils.isEmpty(targetPath.getText())) { - show(null, "请输入路径", Level.WARN); + String remoteUrl = downloadPath.getText(); + String localRepoFile = targetPath.getText(); + if (StringUtils.isAnyBlank(remoteUrl, localRepoFile)) { + show(null, "请检查项目地址和仓库地址不能为空", Level.WARN); } else { - //todo 输入正确,读取文件夹中内容 + // 检查仓库地址是不是为空 + File file = new File(targetPath.getText()); + if (file.isFile() || (file.isDirectory() && file.listFiles().length > 0)) { + show(null, "仓库地址不为空", Level.WARN); + return; + } + // 检查项目是否已经存在 + if (H2PoolUtils.existsGitProjects(JGitUtils.getRepoName(remoteUrl), JGitUtils.getAuthor(remoteUrl))) { + show(null, "项目已经存在", Level.WARN); + return; + } + // 项目保存地址不存在则创建 + boolean newFolder = file.exists(); + if (!newFolder) { + newFolder = file.mkdirs(); + } + if (newFolder) { + // 判断权限信息是否存在 + GitAuthInfo authInfo = JGitUtils.isExistsAuthInfo(remoteUrl); + if (authInfo != null) { + AsyncTask.runOnce("克隆项目", () -> JGitUtils.cloneRepo(remoteUrl, file, authInfo)); + } else { + AuthenticationPane authPane = new AuthenticationPane(JGitUtils.getType(remoteUrl), remoteUrl, file); + authPane.show(getScene()); + } + } } } catch (Exception e) { show(null, "下载失败,请稍后重试", Level.DANGER); } }); - downloadVBox.getChildren().addAll(downloadPath, targetPath, choiceBox, downloadButton); - this.setCenter(downloadVBox); - } + right.setOnMouseClicked(event -> { + dirChooser.setInitialDirectory(new File(INIT_DIR + File.separator)); + File file = dirChooser.showDialog(getScene().getWindow()); + if (null != file) { + targetPath.setText(file.getPath()); + targetFolderPath.set(file.getPath()); + } + event.consume(); + }); + + downloadPath.setOnKeyReleased(event -> { + String targetPathText = StringUtils.isBlank(targetFolderPath.get()) ? "" : targetFolderPath.get() + File.separator; + String downloadPathText = downloadPath.getText(); + if (downloadPathText.contains("/") && !downloadPathText.startsWith("/")) { + String downloadPathFolder = downloadPathText.substring(downloadPathText.lastIndexOf("/") + 1); + String actPath = downloadPathFolder.endsWith(".git") ? downloadPathFolder.substring(0, downloadPathFolder.lastIndexOf(".")) : downloadPathFolder; + if (StringUtils.isNotBlank(targetPathText)) { + targetPath.setText(targetPathText + actPath); + } + downloadButton.setDisable(!downloadPathText.endsWith(".git")); + } else { + targetPath.setText(targetPathText); + downloadButton.setDisable(true); + } + }); + } } \ No newline at end of file diff --git a/src/main/java/com/light/view/ManagerView.java b/src/main/java/com/light/view/ManagerView.java index 32dd5d6d3c817c8b7794e27199dfeababc919aaa..b81404ef74473840ea24e027e24d4f93e797024c 100644 --- a/src/main/java/com/light/view/ManagerView.java +++ b/src/main/java/com/light/view/ManagerView.java @@ -6,27 +6,24 @@ import atlantafx.base.theme.Tweaks; import com.light.component.LevelTableCell; import com.light.component.OperationTableCell; import com.light.component.TooltipTableRow; -import com.light.enums.Level; import com.light.model.GitProject; import com.light.thread.AsyncTask; -import com.light.util.FxDataUtil; +import com.light.util.FxApplicationContextUtils; import com.light.util.H2PoolUtils; -import com.light.util.NoticeUtils; +import com.light.util.JGitUtils; import javafx.collections.FXCollections; import javafx.geometry.Insets; -import javafx.geometry.Pos; -import javafx.scene.Scene; -import javafx.scene.control.*; +import javafx.scene.control.Button; +import javafx.scene.control.CheckBox; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; import javafx.scene.control.cell.CheckBoxTableCell; -import javafx.scene.control.cell.ChoiceBoxTableCell; import javafx.scene.control.cell.ProgressBarTableCell; -import javafx.scene.image.Image; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; -import javafx.stage.Modality; -import javafx.stage.Stage; +import javafx.stage.DirectoryChooser; import org.apache.commons.lang3.StringUtils; import org.kordamp.ikonli.bootstrapicons.BootstrapIcons; import org.kordamp.ikonli.javafx.FontIcon; @@ -40,6 +37,8 @@ public class ManagerView extends StackPane { private CustomTextField searchField; private final Button updateButton = new Button("更新"); private final Button addLocalButton = new Button("添加本地项目"); + public static final String INIT_DIR = "D:"; + private final DirectoryChooser dirChooser = new DirectoryChooser(); private final HBox hBox = new HBox(5); // 表格 @@ -54,12 +53,7 @@ public class ManagerView extends StackPane { initialize(); initEvent(); // 异步初始化数据 - AsyncTask.runOnce(new AsyncTask("init_project_data", "初始化项目数据") { - @Override - protected void handler() throws Exception { - initData(); - } - }); + AsyncTask.runOnce("初始化项目数据", this::initData); } public void initialize() { @@ -89,8 +83,8 @@ public class ManagerView extends StackPane { select.setMaxWidth(40d); select.setMinWidth(40d); - var id = new TableColumn("序号"); - id.setCellValueFactory(c -> c.getValue().id()); + var id = new TableColumn("序号"); + id.setCellValueFactory(c -> c.getValue().id().asObject()); id.setPrefWidth(50d); id.setMaxWidth(50d); id.setMinWidth(50d); @@ -103,7 +97,6 @@ public class ManagerView extends StackPane { var branch = new TableColumn("分支"); branch.setSortable(false); - branch.setCellFactory(ChoiceBoxTableCell.forTableColumn("master", "develop")); branch.setCellValueFactory(c -> c.getValue().branch()); var level = new TableColumn("学习等级"); @@ -115,15 +108,15 @@ public class ManagerView extends StackPane { process.setCellFactory(ProgressBarTableCell.forTableColumn()); process.setCellValueFactory(c -> c.getValue().downloadRate().asObject()); - var operation = new TableColumn("操作"); + var operation = new TableColumn("操作"); operation.setSortable(false); operation.setCellFactory(OperationTableCell.forTableColumn()); - operation.setCellValueFactory(c -> c.getValue().id()); + operation.setCellValueFactory(c -> c.getValue().id().asObject()); operation.setPrefWidth(120d); operation.setMaxWidth(120d); operation.setMinWidth(120d); - tableView = new TableView<>(FxDataUtil.GIT_PROJECT_OBSERVABLE_LIST); + tableView = new TableView<>(FxApplicationContextUtils.GIT_PROJECT_OBSERVABLE_LIST); tableView.getColumns().setAll(select, id, warehouse, author, branch, level, process, operation); tableView.setRowFactory(TooltipTableRow.forTableRow()); tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); @@ -135,7 +128,7 @@ public class ManagerView extends StackPane { } public void initData() { - // TODO 查H2数据库获取数据 + // 查H2数据库获取数据 H2PoolUtils.queryGitProjects(); } @@ -154,51 +147,14 @@ public class ManagerView extends StackPane { * 按钮 加载 */ addLocalButton.setOnAction(btn -> { - //输入框横向布局 - HBox inputHBox = new HBox(10); - inputHBox.setAlignment(Pos.CENTER); - //错误提示横向布局 - HBox noticeHBox = new HBox(10); - noticeHBox.setAlignment(Pos.CENTER); - - //输入框 - TextField textField = new TextField(); - textField.setPromptText("请输入代码存放路径"); - textField.setMinWidth(550); - //设置打开窗口时焦点不在输入框中 - textField.setFocusTraversable(false); - //加载按钮 - Button load = new Button("加载"); - inputHBox.getChildren().addAll(textField, load); - Scene scene = new Scene(inputHBox, 700, 100); - Stage projectPathStage = new Stage(); - projectPathStage.initOwner(getScene().getWindow()); - //设置此窗口在优先级最高 屏蔽其他窗口 - projectPathStage.initModality(Modality.WINDOW_MODAL); - projectPathStage.getIcons().add(new Image("/icons/git.png")); - projectPathStage.setTitle("添加本地项目"); - projectPathStage.setScene(scene); - projectPathStage.show(); - - load.setOnAction(loadBtn -> { - System.out.println("用户输入路径:" + textField.getText()); - //错误提示 - try { - if (StringUtils.isEmpty(textField.getText())) { - NoticeUtils.show(getScene().getWindow(), "请输入路径", Level.WARN); - } else { - File file = new File(textField.getText()); - if (file.exists()) { - //todo 输入正确,读取文件夹中内容 - - } else { - NoticeUtils.show(getScene().getWindow(), "该路径不存在或路径下无内容", Level.DANGER); - } - } - } catch (Exception e) { - NoticeUtils.show(getScene().getWindow(), "路径不正确", Level.DANGER); - } - }); + dirChooser.setInitialDirectory(new File(INIT_DIR + File.separator)); + File file = dirChooser.showDialog(getScene().getWindow()); + if (null != file) { + AsyncTask.runOnce("加载本地项目", () -> { + JGitUtils.parseLocalProjectPath(file); + JGitUtils.parseLocalProject(); + }); + } }); // 搜索框执行逻辑 @@ -206,16 +162,16 @@ public class ManagerView extends StackPane { String text = searchField.getText(); if (StringUtils.isNotBlank(text)) { List list; - if (FxDataUtil.GIT_PROJECT_OBSERVABLE_LIST.size() > 10000) { - list = FxDataUtil.GIT_PROJECT_OBSERVABLE_LIST. - parallelStream().filter(param -> param.name().get().contains(text) || param.author().get().contains(text)).toList(); + if (FxApplicationContextUtils.GIT_PROJECT_OBSERVABLE_LIST.size() > 10000) { + list = FxApplicationContextUtils.GIT_PROJECT_OBSERVABLE_LIST. + parallelStream().filter(param -> param.name().get().toUpperCase().contains(text.toUpperCase()) || param.author().get().toUpperCase().contains(text.toUpperCase())).toList(); } else { - list = FxDataUtil.GIT_PROJECT_OBSERVABLE_LIST. - stream().filter(param -> param.name().get().contains(text) || param.author().get().contains(text)).toList(); + list = FxApplicationContextUtils.GIT_PROJECT_OBSERVABLE_LIST. + stream().filter(param -> param.name().get().toUpperCase().contains(text.toUpperCase()) || param.author().get().toUpperCase().contains(text.toUpperCase())).toList(); } tableView.setItems(FXCollections.observableList(list)); } else { - tableView.setItems(FxDataUtil.GIT_PROJECT_OBSERVABLE_LIST); + tableView.setItems(FxApplicationContextUtils.GIT_PROJECT_OBSERVABLE_LIST); } }); @@ -224,7 +180,7 @@ public class ManagerView extends StackPane { List list = tableView.getItems().stream().filter(param -> param.selected().get()).toList(); if (!list.isEmpty()) { updateButton.setDisable(true); - FxDataUtil.UPDATE_PROPERTY.set(FxDataUtil.UPDATE_NUMBER.addAndGet(list.size())); + FxApplicationContextUtils.UPDATE_PROPERTY.set(String.valueOf(FxApplicationContextUtils.UPDATE_NUMBER.addAndGet(list.size()))); } list.forEach(param -> { param.downloadRate().set(0.0); diff --git a/src/main/java/com/light/view/NotificationView.java b/src/main/java/com/light/view/NotificationView.java index 16e16b218886ed38e8cac18ff4e8246dc024e194..a7870d4ee9ed89c8fdb40e816f533f21d759fa3c 100644 --- a/src/main/java/com/light/view/NotificationView.java +++ b/src/main/java/com/light/view/NotificationView.java @@ -1,13 +1,13 @@ package com.light.view; -import com.light.util.FxDataUtil; +import com.light.util.FxApplicationContextUtils; import javafx.collections.ListChangeListener; import javafx.scene.control.Label; public class NotificationView extends Label { public NotificationView() { - super(FxDataUtil.HISTORY_NOTICE_LIST.size() + ""); + super(FxApplicationContextUtils.HISTORY_NOTICE_LIST.size() + ""); this.getStyleClass().add("notice-label"); - FxDataUtil.HISTORY_NOTICE_LIST.addListener((ListChangeListener) c -> setText(FxDataUtil.HISTORY_NOTICE_LIST.size() + "")); + FxApplicationContextUtils.HISTORY_NOTICE_LIST.addListener((ListChangeListener) c -> setText(FxApplicationContextUtils.HISTORY_NOTICE_LIST.size() + "")); } } diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 7463f1472c34979e2ca7883c648cab7154718d38..3bf9a6fd8420b101c668946796b7e79de1e73f5b 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -12,4 +12,5 @@ open module com.leo { requires org.kordamp.ikonli.antdesignicons; requires org.kordamp.ikonli.bootstrapicons; requires org.kordamp.ikonli.core; + requires org.eclipse.jgit; } diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 3db96d3fc70004cf45c96e94a6b1e36241c9f0b1..c194821669b699dee59b62671ef8b5bc5a26a68c 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -230,7 +230,7 @@ - +