2025年3月31日18:17:15

master
qmstyle 2025-03-31 18:17:28 +08:00
parent b81f2a1801
commit f0a6466feb
23 changed files with 1435 additions and 53 deletions

View File

@ -59,7 +59,7 @@ public class ApiUtils {
public static DataView getUserList() { public static DataView getUserList() {
DataLoad dataLoad = new UserDataLoad(); DataLoad dataLoad = new UserDataLoad();
dataLoad.setForm(new UserForm()); dataLoad.setForm(new UserForm(true));
return dataLoad.loadData(PAGE_NUM,PAGE_SIZE); return dataLoad.loadData(PAGE_NUM,PAGE_SIZE);
} }

View File

@ -8,6 +8,7 @@ import com.zhangmeng.online.exam.ui.components.DynamicTableComponent;
import com.zhangmeng.online.exam.ui.components.callBack.DynamicTableComponentCallBack; import com.zhangmeng.online.exam.ui.components.callBack.DynamicTableComponentCallBack;
import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.SimpleStringProperty;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.PasswordField; import javafx.scene.control.PasswordField;
@ -16,6 +17,7 @@ import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage; import javafx.stage.Stage;
import javafx.stage.Window; import javafx.stage.Window;
import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -31,6 +33,18 @@ public class UserForm extends Form {
private TextField phone_field; private TextField phone_field;
private TextField email_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() { public UserForm() {
Label username_label = new Label("用户名:"); Label username_label = new Label("用户名:");
@ -144,4 +158,12 @@ public class UserForm extends Form {
public void setEmail_field(TextField email_field) { public void setEmail_field(TextField email_field) {
this.email_field = email_field; this.email_field = email_field;
} }
public boolean isIsfxml() {
return isfxml;
}
public void setIsfxml(boolean isfxml) {
this.isfxml = isfxml;
}
} }

View File

@ -94,7 +94,7 @@ public class UserDataLoad implements DataLoad {
@Override @Override
public void editData(String id,Stage stage) { public void editData(String id,Stage stage) {
UserForm userForm = new UserForm(); UserForm userForm = new UserForm(true);
Map<String, Object> map = new HashMap<>(); Map<String, Object> map = new HashMap<>();
map.put("id", id); map.put("id", id);

View File

@ -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.api.form.base.Form;
import com.zhangmeng.online.exam.ui.components.DynamicTableComponent; import com.zhangmeng.online.exam.ui.components.DynamicTableComponent;
import com.zhangmeng.online.exam.ui.utils.AlertUtils; import com.zhangmeng.online.exam.ui.utils.AlertUtils;
import com.zhangmeng.online.exam.ui.utils.FxUtils;
import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.SimpleStringProperty;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.scene.Node; import javafx.scene.Node;
@ -42,9 +43,10 @@ public class DynamicTableComponentCallBackImpl implements DynamicTableComponentC
@Override @Override
public void Add(Parent node, Stage primaryStage, DynamicTableComponent table) { public void Add(Parent node, Stage primaryStage, DynamicTableComponent table) {
Form form = (Form) node; // Form form = (Form) node;
form.setTableComponent(table); // form.setTableComponent(table);
AlertUtils.alert("设置", node, primaryStage); // AlertUtils.alert("设置", node, primaryStage);
FxUtils.alert("设置",node, primaryStage);
} }
@Override @Override

View File

@ -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.LoginPage;
import com.zhangmeng.online.exam.ui.admin.PaperPage; import com.zhangmeng.online.exam.ui.admin.PaperPage;
import com.zhangmeng.online.exam.ui.api.ApiUtils; 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.AlertUtils;
import com.zhangmeng.online.exam.ui.utils.FxUtils;
import com.zhangmeng.online.exam.ui.utils.HttpUtils; import com.zhangmeng.online.exam.ui.utils.HttpUtils;
import javafx.beans.value.ChangeListener; import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import javafx.concurrent.ScheduledService; import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task; import javafx.concurrent.Task;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.scene.control.Alert; import javafx.scene.control.Alert;
import javafx.scene.control.Button; import javafx.scene.control.Button;
@ -21,6 +26,7 @@ import javafx.scene.image.ImageView;
import javafx.stage.Stage; import javafx.stage.Stage;
import javafx.util.Duration; import javafx.util.Duration;
import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -79,8 +85,15 @@ public class LoginController {
Map<String, Object> data = (Map<String, Object>) jsonObject.get("data"); Map<String, Object> data = (Map<String, Object>) jsonObject.get("data");
Object token = data.get("token"); Object token = data.get("token");
HttpUtils.USER_INFO.put("token", 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(); LoginController.MyScheduledService myService = new LoginController.MyScheduledService();
//myService.setDelay(Duration.seconds(5));//延迟 //myService.setDelay(Duration.seconds(5));//延迟
@ -112,7 +125,7 @@ public class LoginController {
System.out.println("lastValueProperty:" + newValue.intValue()); System.out.println("lastValueProperty:" + newValue.intValue());
if (newValue.intValue() == 3) { if (newValue.intValue() == 3) {
myService.cancel(); myService.cancel();
alert.close(); //alert.close();
success(); success();
} }
} }
@ -127,21 +140,10 @@ public class LoginController {
} }
} }
private void user_type() {
Map<String, Object> 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<String, Object> data = (Map<String, Object>) jsonObject.get("data");
String type = data.get("type").toString();
HttpUtils.USER_INFO.put("type", type);
}
}
private void success() { private void success() {
String type = HttpUtils.USER_INFO.get("type").toString(); String type = UserService.getCurrentUser().getRoleType();
switch (type) { switch (type) {
case "STUDENT" -> user_page(); case "STUDENT" -> user_page();
case "ADMIN" -> admin_page(); case "ADMIN" -> admin_page();
@ -166,15 +168,15 @@ public class LoginController {
private void admin_page() { private void admin_page() {
Scene scene = loginButton.getScene(); Scene scene = loginButton.getScene();
Stage window = (Stage) scene.getWindow(); Stage window = (Stage) scene.getWindow();
window.close(); // IndexPage indexPage = new IndexPage();
try {
Stage stage = new Stage(); Parent main = FXMLLoader.load(this.getClass().getResource("/fxml/main.fxml"));
IndexPage indexPage = new IndexPage(); scene = new Scene(main, FxUtils.DEFAULT_WIDTH, FxUtils.DEFAULT_HEIGHT);
window.setScene(scene);
scene = new Scene(indexPage, 1280, 720); window.centerOnScreen();
stage.setScene(scene); } catch (IOException e) {
stage.setTitle("在线考试系统"); throw new RuntimeException(e);
stage.show(); }
} }
class MyScheduledService extends ScheduledService<Number> { class MyScheduledService extends ScheduledService<Number> {

View File

@ -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<MenuData> 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<MenuData> selectedItem = menuTree.getSelectionModel().getSelectedItem();
if (selectedItem != null && selectedItem.isLeaf()) {
openTab(selectedItem.getValue());
}
});
}
private void initializeMenu() {
TreeItem<MenuData> root = new TreeItem<>(new MenuData("系统菜单", null, null));
root.setGraphic(new Label(root.getValue().getTitle()));
root.setExpanded(true);
menuTree.setCellFactory(new Callback<TreeView<MenuData>, TreeCell<MenuData>>() {
@Override
public TreeCell<MenuData> call(TreeView<MenuData> menuDataTreeView) {
return new TreeCell<MenuData>() {
@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();
}
}
}

View File

@ -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<String> statusComboBox;
@FXML
private ListView<Role> 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());
}
}

View File

@ -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) {
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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<Permission> 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<Permission> getChildren() {
return children;
}
public void setChildren(List<Permission> children) {
this.children = children;
}
}

View File

@ -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<Permission> 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<Permission> getPermissions() {
return permissions;
}
public void setPermissions(List<Permission> 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;
}
}

View File

@ -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<Role> 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<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> 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;
}
}

View File

@ -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<LogEntry> 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<OperationLog> 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<OperationLog> 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;
}
}
}

View File

@ -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<Role> getAllRoles() {
Map<String, Object> 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<Role> 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;
}
}

View File

@ -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<String, Object> 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<String, Object> data = (Map<String, Object>) jsonObject.get("data");
String type = data.get("type").toString();
currentUser.setRoleType(type);
}
}
public static void logout() {
currentUser = null;
}
public static List<TreeItem<MenuData>> menu_tree_item() {
String sideData = HttpUtils.GET(ApiUtils.API_URL + "/user/menu");
JSONObject jsonObject = JSON.parseObject(sideData);
JSONArray data = jsonObject.getJSONArray("data");
List<TreeItem<MenuData>> 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<MenuData> 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<MenuData> 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) {
}
}

View File

@ -45,6 +45,7 @@ import java.io.IOException;
*/ */
public class AlertUtils { 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);
}
}
} }

View File

@ -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();
}
}

View File

@ -4,6 +4,7 @@ import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.CharsetUtil;
import cn.hutool.http.HttpUtil; import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.zhangmeng.online.exam.ui.service.UserService;
import java.io.File; import java.io.File;
@ -18,6 +19,7 @@ import java.util.Map;
*/ */
public class HttpUtils<T> { public class HttpUtils<T> {
public static Map<String, Object> USER_INFO = new HashMap<>(); public static Map<String, Object> USER_INFO = new HashMap<>();
public static <T> T GET(String url, Class<T> clazz) { public static <T> T GET(String url, Class<T> clazz) {
@ -26,20 +28,20 @@ public class HttpUtils<T> {
} }
public static String GET(String url) { 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); return HttpUtil.get(url, CharsetUtil.CHARSET_UTF_8);
} }
public static String GET(String url, Map<String, Object> params) { public static String GET(String url, Map<String, Object> params) {
Object token = USER_INFO.get("token"); String token = UserService.getCurrentUser().getToken();
params.put("token",token); params.put("token",token);
return HttpUtil.get(url, params); return HttpUtil.get(url, params);
} }
public static String POST(String url, Map<String, Object> params) { public static String POST(String url, Map<String, Object> params) {
HashMap<String, Object> paramMap = new HashMap<>(params); HashMap<String, Object> paramMap = new HashMap<>(params);
Object token = USER_INFO.get("token"); String token = UserService.getCurrentUser().getToken();
paramMap.put("token",token); paramMap.put("token",token);
return HttpUtil.post(url, paramMap); return HttpUtil.post(url, paramMap);
} }

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<BorderPane xmlns:fx="http://javafx.com/fxml"
fx:controller="com.zhangmeng.online.exam.ui.controller.MainController">
<top>
<HBox spacing="10" alignment="CENTER_RIGHT" style="-fx-padding: 5;">
<Label fx:id="userLabel"/>
<Button text="退出" onAction="#handleLogout"/>
</HBox>
</top>
<left>
<TreeView fx:id="menuTree" prefWidth="200"/>
</left>
<center>
<TabPane fx:id="contentTabPane" tabClosingPolicy="ALL_TABS"/>
</center>
</BorderPane>

View File

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ChoiceBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fmxl.RoleForm">
<children>
<Label layoutX="135.0" layoutY="84.0" text="类型:" />
<Label layoutX="130.0" layoutY="145.0" text="角色名称:" />
<Label layoutX="123.0" layoutY="209.0" text="角色描述:" />
<ChoiceBox layoutX="234.0" layoutY="80.0" prefHeight="23.0" prefWidth="229.0" />
<TextField layoutX="234.0" layoutY="141.0" prefHeight="23.0" prefWidth="229.0" />
<TextField layoutX="234.0" layoutY="205.0" prefHeight="23.0" prefWidth="229.0" />
<Button layoutX="267.0" layoutY="289.0" mnemonicParsing="false" text="保存" />
</children>
</AnchorPane>

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<VBox spacing="10" xmlns:fx="http://javafx.com/fxml"
fx:controller="com.zhangmeng.online.exam.ui.controller.UserEditController">
<padding>
<Insets top="10" right="10" bottom="10" left="10"/>
</padding>
<GridPane hgap="10" vgap="10">
<columnConstraints>
<ColumnConstraints minWidth="80" prefWidth="100"/>
<ColumnConstraints hgrow="ALWAYS"/>
</columnConstraints>
<!-- 用户名 -->
<Label text="用户名:" GridPane.rowIndex="0" GridPane.columnIndex="0"/>
<TextField fx:id="usernameField" GridPane.rowIndex="0" GridPane.columnIndex="1"/>
<!-- 姓名 -->
<Label text="姓名:" GridPane.rowIndex="1" GridPane.columnIndex="0"/>
<TextField fx:id="nameField" GridPane.rowIndex="1" GridPane.columnIndex="1"/>
<!-- 密码 -->
<Label text="密码:" GridPane.rowIndex="2" GridPane.columnIndex="0"/>
<PasswordField fx:id="passwordField" GridPane.rowIndex="2" GridPane.columnIndex="1"/>
<!-- 邮箱 -->
<Label text="邮箱:" GridPane.rowIndex="3" GridPane.columnIndex="0"/>
<TextField fx:id="emailField" GridPane.rowIndex="3" GridPane.columnIndex="1"/>
<!-- 状态 -->
<Label text="状态:" GridPane.rowIndex="4" GridPane.columnIndex="0"/>
<ComboBox fx:id="statusComboBox" GridPane.rowIndex="4" GridPane.columnIndex="1"/>
<!-- 角色 -->
<Label text="角色:" GridPane.rowIndex="5" GridPane.columnIndex="0"/>
<ListView fx:id="roleListView" prefHeight="100" GridPane.rowIndex="5" GridPane.columnIndex="1"/>
</GridPane>
<HBox spacing="10" alignment="CENTER_RIGHT">
<Button text="保存" onAction="#handleSave" defaultButton="true"/>
<Button text="取消" onAction="#handleCancel" cancelButton="true"/>
</HBox>
</VBox>

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<VBox spacing="10" xmlns:fx="http://javafx.com/fxml"
fx:controller="com.zhangmeng.online.exam.ui.controller.UserListController">
<padding>
<Insets top="10" right="10" bottom="10" left="10"/>
</padding>
<children>
<HBox spacing="10" alignment="CENTER_LEFT">
<TextField fx:id="searchField" promptText="输入关键字搜索..." HBox.hgrow="ALWAYS"/>
<Button text="搜索" onAction="#handleSearch"/>
<Region HBox.hgrow="ALWAYS"/>
<Button fx:id="addButton" text="添加用户" onAction="#handleAdd"/>
</HBox>
<TableView fx:id="userTable" VBox.vgrow="ALWAYS">
<columns>
<TableColumn fx:id="idColumn" text="ID" prefWidth="50"/>
<TableColumn fx:id="usernameColumn" text="用户名" prefWidth="100"/>
<TableColumn fx:id="nameColumn" text="姓名" prefWidth="100"/>
<TableColumn fx:id="emailColumn" text="邮箱" prefWidth="150"/>
<TableColumn fx:id="statusColumn" text="状态" prefWidth="80"/>
<TableColumn fx:id="actionColumn" text="操作" prefWidth="150"/>
</columns>
</TableView>
</children>
</VBox>