From f0a6466feba8f9d53ef4042e897a60d60901914f Mon Sep 17 00:00:00 2001 From: qmstyle Date: Mon, 31 Mar 2025 18:17:28 +0800 Subject: [PATCH] =?UTF-8?q?2025=E5=B9=B43=E6=9C=8831=E6=97=A518:17:15?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../online/exam/ui/api/ApiUtils.java | 2 +- .../online/exam/ui/api/form/UserForm.java | 22 +++ .../exam/ui/api/model/UserDataLoad.java | 2 +- .../DynamicTableComponentCallBackImpl.java | 8 +- .../exam/ui/controller/LoginController.java | 52 ++--- .../exam/ui/controller/MainController.java | 184 ++++++++++++++++++ .../ui/controller/UserEditController.java | 183 +++++++++++++++++ .../ui/controller/UserListController.java | 18 ++ .../online/exam/ui/module/MenuData.java | 45 +++++ .../online/exam/ui/module/OperationLog.java | 103 ++++++++++ .../online/exam/ui/module/Permission.java | 90 +++++++++ .../zhangmeng/online/exam/ui/module/Role.java | 106 ++++++++++ .../zhangmeng/online/exam/ui/module/User.java | 158 +++++++++++++++ .../online/exam/ui/service/LogService.java | 179 +++++++++++++++++ .../online/exam/ui/service/RoleService.java | 51 +++++ .../online/exam/ui/service/UserService.java | 98 ++++++++++ .../online/exam/ui/utils/AlertUtils.java | 15 ++ .../online/exam/ui/utils/FxUtils.java | 43 ++++ .../online/exam/ui/utils/HttpUtils.java | 8 +- src/main/resources/fxml/main.fxml | 23 +++ src/main/resources/fxml/role_form.fxml | 20 -- src/main/resources/fxml/user-edit.fxml | 48 +++++ src/main/resources/fxml/user-list.fxml | 30 +++ 23 files changed, 1435 insertions(+), 53 deletions(-) create mode 100644 src/main/java/com/zhangmeng/online/exam/ui/controller/MainController.java create mode 100644 src/main/java/com/zhangmeng/online/exam/ui/controller/UserEditController.java create mode 100644 src/main/java/com/zhangmeng/online/exam/ui/controller/UserListController.java create mode 100644 src/main/java/com/zhangmeng/online/exam/ui/module/MenuData.java create mode 100644 src/main/java/com/zhangmeng/online/exam/ui/module/OperationLog.java create mode 100644 src/main/java/com/zhangmeng/online/exam/ui/module/Permission.java create mode 100644 src/main/java/com/zhangmeng/online/exam/ui/module/Role.java create mode 100644 src/main/java/com/zhangmeng/online/exam/ui/module/User.java create mode 100644 src/main/java/com/zhangmeng/online/exam/ui/service/LogService.java create mode 100644 src/main/java/com/zhangmeng/online/exam/ui/service/RoleService.java create mode 100644 src/main/java/com/zhangmeng/online/exam/ui/service/UserService.java create mode 100644 src/main/java/com/zhangmeng/online/exam/ui/utils/FxUtils.java create mode 100644 src/main/resources/fxml/main.fxml delete mode 100644 src/main/resources/fxml/role_form.fxml create mode 100644 src/main/resources/fxml/user-edit.fxml create mode 100644 src/main/resources/fxml/user-list.fxml diff --git a/src/main/java/com/zhangmeng/online/exam/ui/api/ApiUtils.java b/src/main/java/com/zhangmeng/online/exam/ui/api/ApiUtils.java index 38ff53a..eed5cda 100644 --- a/src/main/java/com/zhangmeng/online/exam/ui/api/ApiUtils.java +++ b/src/main/java/com/zhangmeng/online/exam/ui/api/ApiUtils.java @@ -59,7 +59,7 @@ public class ApiUtils { public static DataView getUserList() { DataLoad dataLoad = new UserDataLoad(); - dataLoad.setForm(new UserForm()); + dataLoad.setForm(new UserForm(true)); return dataLoad.loadData(PAGE_NUM,PAGE_SIZE); } diff --git a/src/main/java/com/zhangmeng/online/exam/ui/api/form/UserForm.java b/src/main/java/com/zhangmeng/online/exam/ui/api/form/UserForm.java index 13f5245..58c2aad 100644 --- a/src/main/java/com/zhangmeng/online/exam/ui/api/form/UserForm.java +++ b/src/main/java/com/zhangmeng/online/exam/ui/api/form/UserForm.java @@ -8,6 +8,7 @@ import com.zhangmeng.online.exam.ui.components.DynamicTableComponent; import com.zhangmeng.online.exam.ui.components.callBack.DynamicTableComponentCallBack; import javafx.beans.property.SimpleStringProperty; import javafx.collections.ObservableList; +import javafx.fxml.FXMLLoader; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.PasswordField; @@ -16,6 +17,7 @@ import javafx.scene.layout.AnchorPane; import javafx.stage.Stage; import javafx.stage.Window; +import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -31,6 +33,18 @@ public class UserForm extends Form { private TextField phone_field; private TextField email_field; + private boolean isfxml = true; + + public UserForm(boolean isfxml) { + this.isfxml = isfxml; + try { + FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/user-edit.fxml")); + this.getChildren().add(fxmlLoader.load()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + public UserForm() { Label username_label = new Label("用户名:"); @@ -144,4 +158,12 @@ public class UserForm extends Form { public void setEmail_field(TextField email_field) { this.email_field = email_field; } + + public boolean isIsfxml() { + return isfxml; + } + + public void setIsfxml(boolean isfxml) { + this.isfxml = isfxml; + } } diff --git a/src/main/java/com/zhangmeng/online/exam/ui/api/model/UserDataLoad.java b/src/main/java/com/zhangmeng/online/exam/ui/api/model/UserDataLoad.java index d5d86c2..272a74e 100644 --- a/src/main/java/com/zhangmeng/online/exam/ui/api/model/UserDataLoad.java +++ b/src/main/java/com/zhangmeng/online/exam/ui/api/model/UserDataLoad.java @@ -94,7 +94,7 @@ public class UserDataLoad implements DataLoad { @Override public void editData(String id,Stage stage) { - UserForm userForm = new UserForm(); + UserForm userForm = new UserForm(true); Map map = new HashMap<>(); map.put("id", id); diff --git a/src/main/java/com/zhangmeng/online/exam/ui/components/callBack/DynamicTableComponentCallBackImpl.java b/src/main/java/com/zhangmeng/online/exam/ui/components/callBack/DynamicTableComponentCallBackImpl.java index 2c8d83a..2fe2ee6 100644 --- a/src/main/java/com/zhangmeng/online/exam/ui/components/callBack/DynamicTableComponentCallBackImpl.java +++ b/src/main/java/com/zhangmeng/online/exam/ui/components/callBack/DynamicTableComponentCallBackImpl.java @@ -6,6 +6,7 @@ import com.zhangmeng.online.exam.ui.api.form.UserForm; import com.zhangmeng.online.exam.ui.api.form.base.Form; import com.zhangmeng.online.exam.ui.components.DynamicTableComponent; import com.zhangmeng.online.exam.ui.utils.AlertUtils; +import com.zhangmeng.online.exam.ui.utils.FxUtils; import javafx.beans.property.SimpleStringProperty; import javafx.collections.ObservableList; import javafx.scene.Node; @@ -42,9 +43,10 @@ public class DynamicTableComponentCallBackImpl implements DynamicTableComponentC @Override public void Add(Parent node, Stage primaryStage, DynamicTableComponent table) { - Form form = (Form) node; - form.setTableComponent(table); - AlertUtils.alert("设置", node, primaryStage); +// Form form = (Form) node; +// form.setTableComponent(table); +// AlertUtils.alert("设置", node, primaryStage); + FxUtils.alert("设置",node, primaryStage); } @Override diff --git a/src/main/java/com/zhangmeng/online/exam/ui/controller/LoginController.java b/src/main/java/com/zhangmeng/online/exam/ui/controller/LoginController.java index cf0b10b..ee712b7 100644 --- a/src/main/java/com/zhangmeng/online/exam/ui/controller/LoginController.java +++ b/src/main/java/com/zhangmeng/online/exam/ui/controller/LoginController.java @@ -5,13 +5,18 @@ import com.zhangmeng.online.exam.ui.admin.IndexPage; import com.zhangmeng.online.exam.ui.admin.LoginPage; import com.zhangmeng.online.exam.ui.admin.PaperPage; import com.zhangmeng.online.exam.ui.api.ApiUtils; +import com.zhangmeng.online.exam.ui.module.User; +import com.zhangmeng.online.exam.ui.service.UserService; import com.zhangmeng.online.exam.ui.utils.AlertUtils; +import com.zhangmeng.online.exam.ui.utils.FxUtils; import com.zhangmeng.online.exam.ui.utils.HttpUtils; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.concurrent.ScheduledService; import javafx.concurrent.Task; import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.control.Alert; import javafx.scene.control.Button; @@ -21,6 +26,7 @@ import javafx.scene.image.ImageView; import javafx.stage.Stage; import javafx.util.Duration; +import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -79,8 +85,15 @@ public class LoginController { Map data = (Map) jsonObject.get("data"); Object token = data.get("token"); HttpUtils.USER_INFO.put("token", token); - new Thread(this::user_type).start(); - Alert alert = AlertUtils.alert_msg(jsonObject.getString("message")); + + User user = new User(); + user.setUsername(username.getText()); + user.setPassword(password.getText()); + user.setToken(token.toString()); + UserService.setCurrentUser(user); + + new Thread(UserService::user_type).start(); +// Alert alert = AlertUtils.alert_msg(jsonObject.getString("message")); LoginController.MyScheduledService myService = new LoginController.MyScheduledService(); //myService.setDelay(Duration.seconds(5));//延迟 @@ -112,7 +125,7 @@ public class LoginController { System.out.println("lastValueProperty:" + newValue.intValue()); if (newValue.intValue() == 3) { myService.cancel(); - alert.close(); + //alert.close(); success(); } } @@ -127,21 +140,10 @@ public class LoginController { } } - private void user_type() { - Map params = new HashMap<>(); - params.put("token", HttpUtils.USER_INFO.get("token")); - String response = HttpUtils.GET(ApiUtils.API_URL + "/user/getUserInfo", params); - JSONObject jsonObject = JSONObject.parseObject(response); - if (jsonObject.getIntValue("code") == 200) { - Map data = (Map) jsonObject.get("data"); - String type = data.get("type").toString(); - HttpUtils.USER_INFO.put("type", type); - } - } + private void success() { - String type = HttpUtils.USER_INFO.get("type").toString(); - + String type = UserService.getCurrentUser().getRoleType(); switch (type) { case "STUDENT" -> user_page(); case "ADMIN" -> admin_page(); @@ -166,15 +168,15 @@ public class LoginController { private void admin_page() { Scene scene = loginButton.getScene(); Stage window = (Stage) scene.getWindow(); - window.close(); - - Stage stage = new Stage(); - IndexPage indexPage = new IndexPage(); - - scene = new Scene(indexPage, 1280, 720); - stage.setScene(scene); - stage.setTitle("在线考试系统"); - stage.show(); +// IndexPage indexPage = new IndexPage(); + try { + Parent main = FXMLLoader.load(this.getClass().getResource("/fxml/main.fxml")); + scene = new Scene(main, FxUtils.DEFAULT_WIDTH, FxUtils.DEFAULT_HEIGHT); + window.setScene(scene); + window.centerOnScreen(); + } catch (IOException e) { + throw new RuntimeException(e); + } } class MyScheduledService extends ScheduledService { diff --git a/src/main/java/com/zhangmeng/online/exam/ui/controller/MainController.java b/src/main/java/com/zhangmeng/online/exam/ui/controller/MainController.java new file mode 100644 index 0000000..bd710d1 --- /dev/null +++ b/src/main/java/com/zhangmeng/online/exam/ui/controller/MainController.java @@ -0,0 +1,184 @@ +package com.zhangmeng.online.exam.ui.controller; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.zhangmeng.online.exam.ui.admin.IndexPage; +import com.zhangmeng.online.exam.ui.admin.UserListPage; +import com.zhangmeng.online.exam.ui.api.ApiUtils; +import com.zhangmeng.online.exam.ui.components.DynamicTableComponent; +import com.zhangmeng.online.exam.ui.components.callBack.DynamicTableComponentCallBackImpl; +import com.zhangmeng.online.exam.ui.layouts.SideMenu; +import com.zhangmeng.online.exam.ui.module.MenuData; +import com.zhangmeng.online.exam.ui.module.User; +import com.zhangmeng.online.exam.ui.utils.FxUtils; +import com.zhangmeng.online.exam.ui.utils.HttpUtils; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.*; +import javafx.scene.layout.HBox; +import javafx.stage.Stage; + +import java.util.ArrayList; +import java.util.List; + +import com.zhangmeng.online.exam.ui.service.UserService; +import javafx.util.Callback; + +/** + * @author zm + * @date 2025/3/31 15:17 + * @version: 1.0 + */ +public class MainController { + + @FXML + private TreeView menuTree; + + @FXML + private TabPane contentTabPane; + + @FXML + private Label userLabel; + + @FXML + public void initialize() { + initializeMenu(); + // 显示当前用户 + User currentUser = UserService.getCurrentUser(); + if (currentUser != null) { + userLabel.setText("当前用户:" + currentUser.getUsername()); + } + + // 添加菜单点击事件 + menuTree.setOnMouseClicked(event -> { + TreeItem selectedItem = menuTree.getSelectionModel().getSelectedItem(); + if (selectedItem != null && selectedItem.isLeaf()) { + openTab(selectedItem.getValue()); + } + }); + } + + private void initializeMenu() { + TreeItem root = new TreeItem<>(new MenuData("系统菜单", null, null)); + root.setGraphic(new Label(root.getValue().getTitle())); + root.setExpanded(true); + + menuTree.setCellFactory(new Callback, TreeCell>() { + @Override + public TreeCell call(TreeView menuDataTreeView) { + return new TreeCell() { + @Override + protected void updateItem(MenuData item, boolean empty) { + super.updateItem(item, empty); + if (!empty) { + HBox hBox = new HBox(); + hBox.setAlignment(Pos.CENTER); + Label label = new Label(item.getTitle()); + hBox.getChildren().addAll(label); + hBox.setMaxWidth(50); + this.setGraphic(hBox); + } else { + this.setGraphic(null); + } + } + }; + } + }); + root.getChildren().addAll(UserService.menu_tree_item()); + menuTree.setRoot(root); + + ContextMenu contextMenu = new ContextMenu(); + MenuItem close_current_tab = new MenuItem("关闭当前标签"); + + close_current_tab.setOnAction(actionEvent -> { + Tab selectedItem = contentTabPane.getSelectionModel().getSelectedItem(); + contentTabPane.getTabs().remove(selectedItem); + }); + + MenuItem close_all_tab = new MenuItem("关闭全部标签"); + close_all_tab.setOnAction(actionEvent -> { + contentTabPane.getTabs().clear(); + }); + + + contextMenu.getItems().add(close_current_tab); + contextMenu.getItems().add(close_all_tab); + contentTabPane.setContextMenu(contextMenu); + } + + private void openTab(MenuData menuData) { + // 检查是否已经存在相同的标签页 + for (Tab tab : contentTabPane.getTabs()) { + MenuData userData = (MenuData) tab.getUserData(); + if (userData.getUrl().equals(menuData.getUrl())) { + contentTabPane.getSelectionModel().select(tab); + return; + } + } + + try { + // 创建新的标签页 + Tab tab = new Tab(menuData.getTitle()); + tab.setUserData(menuData); + + // 根据标题加载不同的内容 + Node content = switch (menuData.getUrl()) { + case "/user/list" -> init_table( ApiUtils.getUserList()); + case "添加用户" -> FXMLLoader.load(getClass().getResource("/fxml/user-edit.fxml")); + case "角色列表" -> FXMLLoader.load(getClass().getResource("/fxml/role-list.fxml")); + case "权限分配" -> FXMLLoader.load(getClass().getResource("/fxml/role-permission.fxml")); + case "基本设置" -> FXMLLoader.load(getClass().getResource("/fxml/setting.fxml")); + case "日志查看" -> FXMLLoader.load(getClass().getResource("/fxml/log.fxml")); + case "公告列表" -> FXMLLoader.load(getClass().getResource("/fxml/notice-list.fxml")); + case "发布公告" -> FXMLLoader.load(getClass().getResource("/fxml/notice-edit.fxml")); + case "在线用户" -> FXMLLoader.load(getClass().getResource("/fxml/monitor-online.fxml")); + case "系统日志" -> FXMLLoader.load(getClass().getResource("/fxml/monitor-log.fxml")); + case "性能监控" -> FXMLLoader.load(getClass().getResource("/fxml/monitor-performance.fxml")); + case "数据监控" -> FXMLLoader.load(getClass().getResource("/fxml/monitor-data.fxml")); + default -> new Label("这是" + menuData.getTitle() + "的内容页面"); + }; + + tab.setContent(content); + contentTabPane.getTabs().add(tab); + contentTabPane.getSelectionModel().select(tab); + } catch (Exception e) { + e.printStackTrace(); + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setTitle("错误"); + alert.setHeaderText(null); + alert.setContentText("加载页面失败:" + e.getMessage()); + alert.showAndWait(); + } + } + + private DynamicTableComponent init_table(ApiUtils.DataView dataView) { + dataView.setPageNum(dataView.getPageNum()); + dataView.setPageSize(dataView.getPageSize()); + dataView.setTotal(dataView.getTotal()); + DynamicTableComponent dynamicTableComponent = new DynamicTableComponent(dataView); + dynamicTableComponent.setCallBack(new DynamicTableComponentCallBackImpl(dataView.getDataLoad(),dataView.getDataLoad().getForm())); + dynamicTableComponent.setPadding(new Insets(15)); + dynamicTableComponent.prefHeightProperty().bind(contentTabPane.heightProperty().subtract(15)); + return dynamicTableComponent; + } + + @FXML + private void handleLogout() { + try { + UserService.logout(); + Parent root = FXMLLoader.load(getClass().getResource("/fxml/login.fxml")); + Stage stage = (Stage) userLabel.getScene().getWindow(); + stage.setScene(new Scene(root)); + stage.centerOnScreen(); + } catch (Exception e) { + e.printStackTrace(); + } + } + +} diff --git a/src/main/java/com/zhangmeng/online/exam/ui/controller/UserEditController.java b/src/main/java/com/zhangmeng/online/exam/ui/controller/UserEditController.java new file mode 100644 index 0000000..b8f0b7b --- /dev/null +++ b/src/main/java/com/zhangmeng/online/exam/ui/controller/UserEditController.java @@ -0,0 +1,183 @@ +package com.zhangmeng.online.exam.ui.controller; + +import com.zhangmeng.online.exam.ui.module.Role; +import com.zhangmeng.online.exam.ui.module.User; +import com.zhangmeng.online.exam.ui.service.LogService; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.collections.transformation.FilteredList; +import javafx.collections.transformation.SortedList; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.control.*; +import javafx.scene.control.cell.CheckBoxListCell; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.scene.layout.HBox; +import javafx.stage.Modality; +import javafx.stage.Stage; +import com.zhangmeng.online.exam.ui.service.RoleService; +import com.zhangmeng.online.exam.ui.service.UserService; + +/** + * @author zm + * @date 2025/3/31 16:59 + * @version: 1.0 + */ +public class UserEditController { + @FXML + private TextField usernameField; + + @FXML + private TextField nameField; + + @FXML + private PasswordField passwordField; + + @FXML + private TextField emailField; + + @FXML + private ComboBox statusComboBox; + + @FXML + private ListView roleListView; + + private User user; + private boolean saveClicked = false; + + @FXML + public void initialize() { + // 初始化状态下拉框 + statusComboBox.getItems().addAll("启用", "禁用"); + statusComboBox.setValue("启用"); + + // 初始化角色列表 + roleListView.setItems(FXCollections.observableArrayList(RoleService.getAllRoles())); + roleListView.setCellFactory(CheckBoxListCell.forListView(role -> { + return role.selectedProperty(); + })); + } + + public void setUser(User user) { + this.user = user; + + usernameField.setText(user.getUsername()); + nameField.setText(user.getName()); + emailField.setText(user.getEmail()); + statusComboBox.setValue(user.getStatus()); + + // 设置已有角色的选中状态 + user.getRoles().forEach(role -> { + role.setSelected(true); + }); + + // 编辑模式下禁用用户名修改 + usernameField.setDisable(true); + // 编辑模式下密码为可选 + passwordField.setPromptText("不修改请留空"); + } + + @FXML + private void handleSave() { + if (!validateInput()) { + return; + } + + try { + if (user == null) { + // 创建新用户 + user = new User(); + user.setUsername(usernameField.getText()); + user.setPassword(passwordField.getText()); + } else if (!passwordField.getText().isEmpty()) { + // 如果输入了新密码,则更新密码 + user.setPassword(passwordField.getText()); + } + + // 更新基本信息 + user.setName(nameField.getText()); + user.setEmail(emailField.getText()); + user.setStatus(statusComboBox.getValue()); + + // 更新角色 + user.getRoles().clear(); + roleListView.getItems().stream() + .filter(Role::isSelected) + .forEach(user::addRole); + + // 保存用户 + if (user.getId() == 0) { + UserService.createUser(user); + LogService.log("用户管理", "创建", "创建用户:" + user.getUsername()); + } else { + UserService.updateUser(user); + LogService.log("用户管理", "更新", "更新用户:" + user.getUsername()); + } + + saveClicked = true; + closeDialog(); + } catch (Exception e) { + LogService.log("用户管理", "保存", "保存用户失败", e); + showError("保存失败", e); + } + } + + @FXML + private void handleCancel() { + closeDialog(); + } + + private boolean validateInput() { + StringBuilder errorMessage = new StringBuilder(); + + if (usernameField.getText() == null || usernameField.getText().trim().isEmpty()) { + errorMessage.append("用户名不能为空!\n"); + } + if (nameField.getText() == null || nameField.getText().trim().isEmpty()) { + errorMessage.append("姓名不能为空!\n"); + } + if (user == null && (passwordField.getText() == null || passwordField.getText().trim().isEmpty())) { + errorMessage.append("密码不能为空!\n"); + } + if (emailField.getText() == null || emailField.getText().trim().isEmpty()) { + errorMessage.append("邮箱不能为空!\n"); + } + if (roleListView.getItems().stream().noneMatch(Role::isSelected)) { + errorMessage.append("至少选择一个角色!\n"); + } + + if (errorMessage.length() == 0) { + return true; + } else { + showError("输入错误", errorMessage.toString()); + return false; + } + } + + private void closeDialog() { + Stage stage = (Stage) usernameField.getScene().getWindow(); + stage.close(); + } + + public User getUser() { + return user; + } + + public boolean isSaveClicked() { + return saveClicked; + } + + private void showError(String title, String message) { + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setTitle(title); + alert.setHeaderText(null); + alert.setContentText(message); + alert.showAndWait(); + } + + private void showError(String title, Exception e) { + showError(title, e.getMessage()); + } + +} diff --git a/src/main/java/com/zhangmeng/online/exam/ui/controller/UserListController.java b/src/main/java/com/zhangmeng/online/exam/ui/controller/UserListController.java new file mode 100644 index 0000000..4755bc9 --- /dev/null +++ b/src/main/java/com/zhangmeng/online/exam/ui/controller/UserListController.java @@ -0,0 +1,18 @@ +package com.zhangmeng.online.exam.ui.controller; + +import javafx.event.ActionEvent; + +/** + * @author zm + * @date 2025/3/31 16:34 + * @version: 1.0 + */ +public class UserListController { + public void handleSearch(ActionEvent actionEvent) { + + } + + public void handleAdd(ActionEvent actionEvent) { + + } +} diff --git a/src/main/java/com/zhangmeng/online/exam/ui/module/MenuData.java b/src/main/java/com/zhangmeng/online/exam/ui/module/MenuData.java new file mode 100644 index 0000000..5ee7a3c --- /dev/null +++ b/src/main/java/com/zhangmeng/online/exam/ui/module/MenuData.java @@ -0,0 +1,45 @@ +package com.zhangmeng.online.exam.ui.module; + +/** + * @author zm + * @date 2025/3/31 15:58 + * @version: 1.0 + */ +public class MenuData { + + private String title; + + private String icon; + + private String url; + + public MenuData(String title, String icon, String url) { + this.title = title; + this.icon = icon; + this.url = url; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getIcon() { + return icon; + } + + public void setIcon(String icon) { + this.icon = icon; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } +} diff --git a/src/main/java/com/zhangmeng/online/exam/ui/module/OperationLog.java b/src/main/java/com/zhangmeng/online/exam/ui/module/OperationLog.java new file mode 100644 index 0000000..cdfb360 --- /dev/null +++ b/src/main/java/com/zhangmeng/online/exam/ui/module/OperationLog.java @@ -0,0 +1,103 @@ +package com.zhangmeng.online.exam.ui.module; + +import java.time.LocalDateTime; + +public class OperationLog { + private int id; + private String username; // 操作用户 + private String module; // 操作模块 + private String action; // 操作类型 + private String content; // 操作内容 + private String ip; // 操作IP + private LocalDateTime operateTime; // 操作时间 + private String status; // 操作状态(成功/失败) + private String errorMsg; // 错误信息 + + public OperationLog() { + } + + public OperationLog(int id, String module, String action, String content, + String ip, LocalDateTime operateTime, String status, String errorMsg) { + this.id = id; + this.module = module; + this.action = action; + this.content = content; + this.ip = ip; + this.operateTime = operateTime; + this.status = status; + this.errorMsg = errorMsg; + } + + // Getters and Setters + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getModule() { + return module; + } + + public void setModule(String module) { + this.module = module; + } + + public String getAction() { + return action; + } + + public void setAction(String action) { + this.action = action; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public LocalDateTime getOperateTime() { + return operateTime; + } + + public void setOperateTime(LocalDateTime operateTime) { + this.operateTime = operateTime; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getErrorMsg() { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) { + this.errorMsg = errorMsg; + } +} \ No newline at end of file diff --git a/src/main/java/com/zhangmeng/online/exam/ui/module/Permission.java b/src/main/java/com/zhangmeng/online/exam/ui/module/Permission.java new file mode 100644 index 0000000..f959fd9 --- /dev/null +++ b/src/main/java/com/zhangmeng/online/exam/ui/module/Permission.java @@ -0,0 +1,90 @@ +package com.zhangmeng.online.exam.ui.module; + +import java.util.ArrayList; +import java.util.List; + +public class Permission { + private int id; + private String code; // 权限代码 + private String name; // 权限名称 + private String type; // 权限类型(菜单、按钮等) + private int parentId; // 父权限ID + private String url; // 资源路径 + private int sort; // 排序号 + private List children = new ArrayList<>(); + + public Permission(int id, String code, String name, String type, int parentId, String url, int sort) { + this.id = id; + this.code = code; + this.name = name; + this.type = type; + this.parentId = parentId; + this.url = url; + this.sort = sort; + } + + // Getters and Setters + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public int getParentId() { + return parentId; + } + + public void setParentId(int parentId) { + this.parentId = parentId; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public int getSort() { + return sort; + } + + public void setSort(int sort) { + this.sort = sort; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } +} \ No newline at end of file diff --git a/src/main/java/com/zhangmeng/online/exam/ui/module/Role.java b/src/main/java/com/zhangmeng/online/exam/ui/module/Role.java new file mode 100644 index 0000000..43f3745 --- /dev/null +++ b/src/main/java/com/zhangmeng/online/exam/ui/module/Role.java @@ -0,0 +1,106 @@ +package com.zhangmeng.online.exam.ui.module; + +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author zm + * @date 2025/3/31 17:05 + * @version: 1.0 + */ +public class Role { + private int id; + private String name; + private String description; + private String status; + private List permissions; + private BooleanProperty selected; + + public Role(int id, String name, String description, String status) { + this.id = id; + this.name = name; + this.description = description; + this.status = status; + this.permissions = new ArrayList<>(); + this.selected = new SimpleBooleanProperty(false); + } + + public Role() { + } + + // Getters and setters + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public List getPermissions() { + return permissions; + } + + public void setPermissions(List permissions) { + this.permissions = new ArrayList<>(permissions); + } + + public void addPermission(Permission permission) { + if (!permissions.contains(permission)) { + permissions.add(permission); + } + } + + public void removePermission(Permission permission) { + permissions.remove(permission); + } + + public boolean hasPermission(String permissionCode) { + return permissions.stream() + .anyMatch(p -> p.getCode().equals(permissionCode)); + } + + public BooleanProperty selectedProperty() { + return selected; + } + + public boolean isSelected() { + return selected.get(); + } + + public void setSelected(boolean selected) { + this.selected.set(selected); + } + + @Override + public String toString() { + return name; + } +} \ No newline at end of file diff --git a/src/main/java/com/zhangmeng/online/exam/ui/module/User.java b/src/main/java/com/zhangmeng/online/exam/ui/module/User.java new file mode 100644 index 0000000..61bdfa2 --- /dev/null +++ b/src/main/java/com/zhangmeng/online/exam/ui/module/User.java @@ -0,0 +1,158 @@ +package com.zhangmeng.online.exam.ui.module; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author zm + * @date 2025/3/31 15:33 + * @version: 1.0 + */ +public class User { + + private int id; + private String username; + private String name; + private String email; + private String status; + private String password; // 加密后的密码 + private String salt; // 密码盐值 + private List roles = new ArrayList<>(); + private String phone; + private String avatarUrl; + + private String token; + + private String roleType; + + + public User(int id, String username, String name, String email, String status) { + this.id = id; + this.username = username; + this.name = name; + this.email = email; + this.status = status; + } + + public User() { + } + + public User(int id, String username, String name, String email, String status, String password, String salt) { + this(id, username, name, email, status); + this.password = password; + this.salt = salt; + } + + // Getters and Setters + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getSalt() { + return salt; + } + + public void setSalt(String salt) { + this.salt = salt; + } + + public List getRoles() { + return roles; + } + + public void setRoles(List roles) { + this.roles = roles; + } + + public void addRole(Role role) { + if (!roles.contains(role)) { + roles.add(role); + } + } + + public boolean hasPermission(String permissionCode) { + return roles.stream() + .anyMatch(role -> role.hasPermission(permissionCode)); + } + + public boolean isEnabled() { + return "启用".equals(status); + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getAvatarUrl() { + return avatarUrl; + } + + public void setAvatarUrl(String avatarUrl) { + this.avatarUrl = avatarUrl; + } + + + public String getRoleType() { + return roleType; + } + + public void setRoleType(String roleType) { + this.roleType = roleType; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } +} diff --git a/src/main/java/com/zhangmeng/online/exam/ui/service/LogService.java b/src/main/java/com/zhangmeng/online/exam/ui/service/LogService.java new file mode 100644 index 0000000..d50ce32 --- /dev/null +++ b/src/main/java/com/zhangmeng/online/exam/ui/service/LogService.java @@ -0,0 +1,179 @@ +package com.zhangmeng.online.exam.ui.service; + + +import com.zhangmeng.online.exam.ui.module.OperationLog; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class LogService { + private static final List logs = new ArrayList<>(); + private static final int MAX_LOGS = 10000; // 最多保存10000条日志 + + /** + * 记录日志 + */ + public static void log(String module, String operation, String message) { + log(module, operation, message, null); + } + + /** + * 记录带异常的日志 + */ + public static void log(String module, String operation, String message, Exception e) { + LogEntry entry = new LogEntry( + module, + operation, + message, + e != null ? e.getMessage() : null, + LocalDateTime.now() + ); + + synchronized (logs) { + logs.add(entry); + if (logs.size() > MAX_LOGS) { + logs.remove(0); + } + } + } + + /** + * 搜索日志 + */ + public static List searchLogs(String keyword) { + return logs.stream() + .filter(log -> keyword == null || keyword.isEmpty() || log.getMessage().contains(keyword)) + .map(log -> new OperationLog( + 0, // Assuming ID is not used or set elsewhere + log.getModule(), + log.getOperation(), + log.getMessage(), + "127.0.0.1", // Placeholder for IP + log.getTime(), + "成功", // Placeholder for status + log.getError() // Assuming error message is used as errorMsg + )) + .collect(Collectors.toList()); + } + + public static List searchLogs(String keyword, String type, LocalDateTime startDate, LocalDateTime endDate) { + + return logs.stream() + + .filter(log -> { + + // 检查日期范围 + + if (startDate != null && log.getTime().isBefore(startDate)) { + + return false; + + } + + if (endDate != null && log.getTime().isAfter(endDate)) { + + return false; + + } + + + + // 检查日志类型 + + if (type != null && !type.isEmpty() && !log.getModule().equals(type)) { + + return false; + + } + + + + // 检查关键字 + + if (keyword != null && !keyword.isEmpty() && !log.getMessage().contains(keyword)) { + + return false; + + } + + + + return true; + + }) + + .map(log -> new OperationLog( + + 0, // Assuming ID is not used or set elsewhere + + log.getModule(), + + log.getOperation(), + + log.getMessage(), + + "127.0.0.1", // Placeholder for IP + + log.getTime(), + + "成功", // Placeholder for status + + log.getError() // Assuming error message is used as errorMsg + + )) + + .collect(Collectors.toList()); + + } + + /** + * 清空日志 + */ + public static void clearLogs() { + synchronized (logs) { + logs.clear(); + } + } + + /** + * 日志条目类 + */ + private static class LogEntry { + private final String module; + private final String operation; + private final String message; + private final String error; + private final LocalDateTime time; + + public LogEntry(String module, String operation, String message, + String error, LocalDateTime time) { + this.module = module; + this.operation = operation; + this.message = message; + this.error = error; + this.time = time; + } + + public String getModule() { + return module; + } + + public String getOperation() { + return operation; + } + + public String getMessage() { + return message; + } + + public String getError() { + return error; + } + + public LocalDateTime getTime() { + return time; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/zhangmeng/online/exam/ui/service/RoleService.java b/src/main/java/com/zhangmeng/online/exam/ui/service/RoleService.java new file mode 100644 index 0000000..2027307 --- /dev/null +++ b/src/main/java/com/zhangmeng/online/exam/ui/service/RoleService.java @@ -0,0 +1,51 @@ +package com.zhangmeng.online.exam.ui.service; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.zhangmeng.online.exam.ui.api.ApiUtils; +import com.zhangmeng.online.exam.ui.module.Role; +import com.zhangmeng.online.exam.ui.module.User; +import com.zhangmeng.online.exam.ui.utils.HttpUtils; +import javafx.beans.property.SimpleStringProperty; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.zhangmeng.online.exam.ui.api.ApiUtils.API_URL; + +/** + * @author zm + * @date 2025/3/31 17:04 + * @version: 1.0 + */ +public class RoleService { + public static List getAllRoles() { + + Map params = new HashMap<>(); + params.put("pageNum", ApiUtils.PAGE_NUM); + params.put("pageSize", ApiUtils.PAGE_SIZE); + + String userListData = HttpUtils.GET(API_URL + "/role/list",params); + JSONObject jsonObject = JSON.parseObject(userListData); + JSONArray data = jsonObject.getJSONArray("data"); + Integer total = jsonObject.getInteger("total"); + List roles = new ArrayList<>(); + for (Object datum : data) { + JSONObject rolemap = (JSONObject) datum; + + Role role = new Role(); + role.setId(rolemap.getInteger("id")); + role.setName(rolemap.getString("name")); + role.setDescription(rolemap.getString("desc")); // 角色描述 + role.setStatus(rolemap.getString("status")); + role.setPermissions(new ArrayList<>()); // 角色权限 + + roles.add(role); + } + + return roles; + } +} diff --git a/src/main/java/com/zhangmeng/online/exam/ui/service/UserService.java b/src/main/java/com/zhangmeng/online/exam/ui/service/UserService.java new file mode 100644 index 0000000..aa2fe0c --- /dev/null +++ b/src/main/java/com/zhangmeng/online/exam/ui/service/UserService.java @@ -0,0 +1,98 @@ +package com.zhangmeng.online.exam.ui.service; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.zhangmeng.online.exam.ui.api.ApiUtils; +import com.zhangmeng.online.exam.ui.module.MenuData; +import com.zhangmeng.online.exam.ui.module.User; +import com.zhangmeng.online.exam.ui.utils.HttpUtils; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.Label; +import javafx.scene.control.TreeItem; +import javafx.stage.Stage; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 模块 + * @author zm + * @date 2025/3/31 15:29 + * @version: 1.0 + */ +public class UserService { + + private static User currentUser; + + public static User getCurrentUser() { + + if (currentUser == null){ + return new User(); + } + + return currentUser; + } + + public static void setCurrentUser(User user) { + currentUser = user; + } + + public static void user_type() { + Map params = new HashMap<>(); + params.put("token", currentUser.getToken()); + String response = HttpUtils.GET(ApiUtils.API_URL + "/user/getUserInfo", params); + JSONObject jsonObject = JSONObject.parseObject(response); + if (jsonObject.getIntValue("code") == 200) { + Map data = (Map) jsonObject.get("data"); + String type = data.get("type").toString(); + currentUser.setRoleType(type); + } + } + + public static void logout() { + currentUser = null; + } + + public static List> menu_tree_item() { + + String sideData = HttpUtils.GET(ApiUtils.API_URL + "/user/menu"); + JSONObject jsonObject = JSON.parseObject(sideData); + JSONArray data = jsonObject.getJSONArray("data"); + + List> menuList = new ArrayList<>(); + for (int i = 0; i < data.size(); i++) { + JSONObject menu = data.getJSONObject(i); + String name = menu.getString("name"); + String url = menu.getString("url"); + TreeItem management = new TreeItem<>(new MenuData(name, null, url)); + management.setGraphic(new Label(management.getValue().getTitle())); + + JSONArray children = (JSONArray) menu.get("children"); + children.forEach(child -> { + JSONObject childMenu = (JSONObject) child; + String childName = childMenu.getString("name"); + String childUrl = childMenu.getString("url"); + TreeItem menuDataTreeItem = new TreeItem<>(new MenuData(childName, null, childUrl)); + menuDataTreeItem.setGraphic(new Label(menuDataTreeItem.getValue().getTitle())); + management.getChildren().add(menuDataTreeItem); + }); + menuList.add(management); + } + + return menuList; + + } + + public static void createUser(User user) { + + } + + public static void updateUser(User user) { + + } +} diff --git a/src/main/java/com/zhangmeng/online/exam/ui/utils/AlertUtils.java b/src/main/java/com/zhangmeng/online/exam/ui/utils/AlertUtils.java index 6ea4757..82937fa 100644 --- a/src/main/java/com/zhangmeng/online/exam/ui/utils/AlertUtils.java +++ b/src/main/java/com/zhangmeng/online/exam/ui/utils/AlertUtils.java @@ -45,6 +45,7 @@ import java.io.IOException; */ public class AlertUtils { + /** * 警告弹窗 */ @@ -87,4 +88,18 @@ public class AlertUtils { }); } + + public static void alert(String title, FXMLLoader loader, Stage primaryStage){ + try { + Scene scene = new Scene(loader.load()); + Stage dialogStage = new Stage(); + dialogStage.setTitle(title); + dialogStage.initModality(Modality.WINDOW_MODAL); + dialogStage.initOwner(primaryStage); + dialogStage.setScene(scene); + dialogStage.showAndWait(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } } diff --git a/src/main/java/com/zhangmeng/online/exam/ui/utils/FxUtils.java b/src/main/java/com/zhangmeng/online/exam/ui/utils/FxUtils.java new file mode 100644 index 0000000..5bb4cbd --- /dev/null +++ b/src/main/java/com/zhangmeng/online/exam/ui/utils/FxUtils.java @@ -0,0 +1,43 @@ +package com.zhangmeng.online.exam.ui.utils; + +import com.zhangmeng.online.exam.ui.api.form.RoleForm; +import com.zhangmeng.online.exam.ui.api.form.UserForm; +import com.zhangmeng.online.exam.ui.api.form.base.Form; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Modality; +import javafx.stage.Stage; + +import java.io.IOException; + +/** + * @author zm + * @date 2025/3/31 15:48 + * @version: 1.0 + */ +public class FxUtils { + + public final static double DEFAULT_WIDTH = 1280; + public final static double DEFAULT_HEIGHT = 720; + + public static void alert(String title, Parent node, Stage primaryStage) { + Form form = (Form) node; + if (form instanceof UserForm) { + form = new UserForm(true); + } + if (form instanceof RoleForm) { + form = new RoleForm(); + } + + Scene scene = new Scene(form); + Stage dialogStage = new Stage(); + dialogStage.setTitle(title); + dialogStage.setResizable(false); + dialogStage.initModality(Modality.WINDOW_MODAL); + dialogStage.initOwner(primaryStage); + dialogStage.setScene(scene); + dialogStage.showAndWait(); + + } +} diff --git a/src/main/java/com/zhangmeng/online/exam/ui/utils/HttpUtils.java b/src/main/java/com/zhangmeng/online/exam/ui/utils/HttpUtils.java index 20ae58f..3dae62b 100644 --- a/src/main/java/com/zhangmeng/online/exam/ui/utils/HttpUtils.java +++ b/src/main/java/com/zhangmeng/online/exam/ui/utils/HttpUtils.java @@ -4,6 +4,7 @@ import cn.hutool.core.io.FileUtil; import cn.hutool.core.util.CharsetUtil; import cn.hutool.http.HttpUtil; import com.alibaba.fastjson2.JSONObject; +import com.zhangmeng.online.exam.ui.service.UserService; import java.io.File; @@ -18,6 +19,7 @@ import java.util.Map; */ public class HttpUtils { + public static Map USER_INFO = new HashMap<>(); public static T GET(String url, Class clazz) { @@ -26,20 +28,20 @@ public class HttpUtils { } public static String GET(String url) { - url = url + "?token=" + USER_INFO.get("token"); + url = url + "?token=" + UserService.getCurrentUser().getToken(); return HttpUtil.get(url, CharsetUtil.CHARSET_UTF_8); } public static String GET(String url, Map params) { - Object token = USER_INFO.get("token"); + String token = UserService.getCurrentUser().getToken(); params.put("token",token); return HttpUtil.get(url, params); } public static String POST(String url, Map params) { HashMap paramMap = new HashMap<>(params); - Object token = USER_INFO.get("token"); + String token = UserService.getCurrentUser().getToken(); paramMap.put("token",token); return HttpUtil.post(url, paramMap); } diff --git a/src/main/resources/fxml/main.fxml b/src/main/resources/fxml/main.fxml new file mode 100644 index 0000000..07d098a --- /dev/null +++ b/src/main/resources/fxml/main.fxml @@ -0,0 +1,23 @@ + + + + + + + + + +