文章目录
- 前言
- 正文
- 一、最终效果
- 1.1 主页面
- 1.2 动物管理页面-初始化
- 1.3 动物管理页面-修改&新增
- 1.4 动物管理页面-删除&批量删除
- 二、核心代码展示
- 2.1 启动类
- 2.2 数据库配置-db.setting
- 2.3 日志文本域组件
- 2.4 自定义表格视图组件
- 2.5 自定义分页组件
- 2.6 动物管理页面
- 2.7 页面工厂
- 附录
- 附1:sql脚本
前言
自学一下Java FX ,用Java代码写一个简易的客户端。
本文项目是Maven项目,使用了Java17,以及mysql。
代码仓库:https://gitee.com/fengsoshuai/java-fx-management-system-demo/tree/dev-with-db/
正文
一、最终效果
1.1 主页面
启动项目后,自动展示主页面。
1.2 动物管理页面-初始化
支持分页查询,搜索,新增,修改,删除,批量删除等功能。
整体分为以下几层:
- 操作按钮,包含新增,修改,删除,刷新列表;
- 搜索框,支持搜索动物的几个常用信息;
- 列表,展示动物数据(分页);
- 分页功能相关按钮,支持首页,下一页,最后一页,跳转到指定页,切换每页大小;
- 操作记录,记录操作日志,内存存储(有兴趣的可以改为数据库存储),支持清空记录;
1.3 动物管理页面-修改&新增
1.4 动物管理页面-删除&批量删除
使用ctrl+鼠标左键选择要删除的数据。然后点击删除按钮。
出现弹窗后,点击确定,即可删除。
二、核心代码展示
2.1 启动类
渲染主页,左侧菜单栏,以及基本的布局。
package org.feng.demofx;
import javafx.application.Application;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.Screen;
import javafx.stage.Stage;
import org.feng.demofx.pages.AnimalManagePage;
import org.feng.demofx.pages.MainPage;
import org.feng.demofx.sys.*;
import org.feng.demofx.util.ArrayUtil;
import org.feng.demofx.util.StyleUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 启动类
*
* @author feng
*/
public class StartApplication extends Application {
/**
* 屏幕宽度
*/
public static final Double SCREEN_WIDTH;
/**
* 屏幕高度
*/
public static final Double SCREEN_HEIGHT;
/**
* 左侧菜单宽度
*/
public static final Double LEFT_MENU_WIDTH;
/**
* root容器,使用HBox类型
*/
private HBox root;
/**
* 左侧菜单栏
*/
private VBox leftMenu;
/**
* 当前页的节点
*/
private Node currentPageNode;
/**
* 左侧菜单的当前索引
*/
private Integer currentMenuIndex;
/**
* 左侧菜单的临时索引,选中时的菜单项索引
*/
private Integer tempIndex;
/**
* 左侧菜单面板背景颜色
*/
private static final Color LEFT_MENU_BACKGROUND_COLOR = Color.web("#FFF8DC");
/**
* 左侧菜单按钮背景颜色
*/
private static final Color LEFT_MENU_BUTTON_BACKGROUND_COLOR = Color.web("#DEB887");
/**
* 左侧菜单按钮hover时(鼠标移动到按钮上)的颜色
*/
private static final Color LEFT_MENU_BUTTON_HOVER_BACKGROUND_COLOR = Color.web("D2691E");
/**
* 左侧菜单按钮点击时的字体颜色
*/
private static final Color LEFT_MENU_BUTTON_CLICK_BACKGROUND_COLOR = Color.web("#6495ed");
static {
SCREEN_WIDTH = Screen.getPrimary().getBounds().getWidth();
SCREEN_HEIGHT = Screen.getPrimary().getBounds().getHeight();
LEFT_MENU_WIDTH = 240.0;
}
@Override
public void start(Stage stage) throws Exception {
// 注册页面渲染器
registerPageNodeRender();
root = new HBox();
// 渲染左侧菜单栏
leftMenu = renderLeftMenu(root);
root.getChildren().add(leftMenu);
// 主面板
currentPageNode = routePage(root, MainPage.PAGE_KEY);
HBox.setHgrow(currentPageNode, Priority.ALWAYS);
root.getChildren().add(currentPageNode);
StyleUtil.setPaneBackground(root, Color.WHITE);
Scene scene = new Scene(root, SCREEN_WIDTH * 0.8, SCREEN_HEIGHT * 0.8);
// 窗口的标题
stage.setTitle("管理系统");
stage.setScene(scene);
// 禁止拖拽窗口大小
stage.setResizable(false);
stage.show();
}
private VBox renderLeftMenu(HBox root) {
VBox vbox = new VBox();
vbox.setMinHeight(root.getPrefHeight());
vbox.setMinWidth(LEFT_MENU_WIDTH);
StyleUtil.setPaneBackground(vbox, LEFT_MENU_BACKGROUND_COLOR);
// 增加菜单中的项目
vbox.getChildren().addAll(getLeftMenuItemList());
return vbox;
}
/**
* 生成左侧菜单按钮
*/
private List<Button> getLeftMenuItemList() {
double buttonHeight = 30;
List<Button> buttonList = new ArrayList<>(3);
Map<String, String> pageNameMap = PageNodeRenderFactory.getPageNameMap();
String[] itemNames = pageNameMap.values().toArray(new String[0]);
for (String name : itemNames) {
Button button = new Button(name);
button.setMinWidth(LEFT_MENU_WIDTH);
button.setMinHeight(buttonHeight);
StyleUtil.setButtonBackground(button, LEFT_MENU_BUTTON_BACKGROUND_COLOR, Color.WHITE);
// 增加鼠标移动到菜单上到hover效果
button.setOnMouseMoved(event -> {
StyleUtil.setButtonBackground(button, LEFT_MENU_BUTTON_HOVER_BACKGROUND_COLOR, Color.WHITE);
StyleUtil.setFont(button, Color.web("#E6A23C"), -1);
});
button.setOnMouseExited(event -> {
if (currentMenuIndex == null || !button.getText().equals(itemNames[currentMenuIndex])) {
StyleUtil.setButtonBackground(button, LEFT_MENU_BUTTON_BACKGROUND_COLOR, Color.WHITE);
} else {
StyleUtil.setButtonBackground(button, LEFT_MENU_BUTTON_BACKGROUND_COLOR, Color.WHITE);
}
});
button.setOnMouseClicked(event -> {
currentMenuIndex = ArrayUtil.getIndexForArray(itemNames, button.getText());
currentPageNode = routePage(root, PageNodeRenderFactory.getPageKeyByName(name));
root.getChildren().remove(1); //清除右侧页面路由组件节点
HBox.setHgrow(currentPageNode, Priority.ALWAYS);
root.getChildren().add(currentPageNode);
StyleUtil.setFont(button, Color.WHITE, -1);
// 选中状态逻辑
if (tempIndex != null) {
Button node = (Button) leftMenu.getChildren().get(tempIndex);
// 清空选中状态样式
StyleUtil.setFont(node, Color.WHITE, -1);
StyleUtil.setButtonBackground(node, LEFT_MENU_BUTTON_BACKGROUND_COLOR, Color.WHITE);
}
// 设置选中样式
StyleUtil.setFont(button, LEFT_MENU_BUTTON_CLICK_BACKGROUND_COLOR, -1);
tempIndex = currentMenuIndex;
});
buttonList.add(button);
}
return buttonList;
}
/**
* 右侧页面路由
*/
private Node routePage(Pane root, String pageKey) {
return PageNodeRenderFactory.newInstance(pageKey).render(root);
}
private void registerPageNodeRender() {
PageNodeRenderFactory.registerNodeRender(MainPage.class);
PageNodeRenderFactory.registerNodeRender(AnimalManagePage.class);
PageNodeRenderFactory.fillPageNameMap();
}
public static void main(String[] args) {
launch();
}
}
2.2 数据库配置-db.setting
#------------------------------------------------------------------------------------------
## 基本配置信息
# JDBC URL,根据不同的数据库,使用相应的JDBC连接字符串
url = jdbc:mysql://localhost:3306/fx_demo?useUnicode=true&serverTimezone=UTC
# 用户名,此处也可以使用 user 代替
username = root
# 密码,此处也可以使用 pass 代替
password = root
# JDBC驱动名,可选(Hutool会自动识别)
# driver = com.mysql.jdbc.Driver
## 可选配置
# 是否在日志中显示执行的SQL
showSql = true
# 是否格式化显示的SQL
formatSql = false
# 是否显示SQL参数
showParams = true
# 打印SQL的日志等级,默认debug
sqlLevel = debug
#------------------------------------------------------------------------------------------
2.3 日志文本域组件
对应于效果中的“操作记录”文本域。
package org.feng.demofx.pages;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import lombok.Getter;
import org.feng.demofx.util.ControlUtil;
import org.feng.demofx.util.TimeUtil;
import java.time.LocalDateTime;
/**
* 日志文本域组件
*
* @author feng
*/
@Getter
public class LogTextAreaComponent {
private final TextArea textArea;
private final VBox logBox;
public LogTextAreaComponent(double height, double width) {
// 控制台输入操作记录
textArea = new TextArea();
textArea.setScrollTop(Double.MAX_VALUE);
textArea.setMaxHeight(height);
textArea.setMaxWidth(width);
textArea.setEditable(false);
textArea.setStyle("-fx-text-fill: red");
// 日志盒子
logBox = new VBox();
logBox.setMaxWidth(width);
logBox.getChildren().add(new Label(""));
Label operLogLabel = new Label("操作记录:");
operLogLabel.setFont(Font.font(null, FontWeight.BOLD, 15));
operLogLabel.setPadding(new Insets(5, 0, 5, 0));
logBox.getChildren().add(operLogLabel);
logBox.getChildren().add(textArea);
// 清空日志记录按钮
logBox.getChildren().add(new Label(""));
Button clearLog = ControlUtil.genClearLogButton();
logBox.getChildren().add(clearLog);
clearLog.setOnMouseClicked(event -> {
textArea.setText("");
});
}
/**
* 记录日志
*
* @param text 日志内容
*/
public void log(String text) {
textArea.appendText("【" + TimeUtil.parse(LocalDateTime.now()) + "】" + text + "\r\n");
}
}
2.4 自定义表格视图组件
package org.feng.demofx.pages;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableView;
import lombok.Data;
import org.feng.demofx.StartApplication;
import org.feng.demofx.util.TableColumnUtil;
import org.feng.demofx.vo.AnimalVo;
import java.util.List;
/**
* 自定义表格视图组件
*
* @author 01434188
*/
@Data
public class CustomTableViewComponent<T> {
/**
* 表格视图
*/
private TableView<T> tableView;
private ObservableList<T> data;
public CustomTableViewComponent(Class<T> beanClass, List<T> dataList) {
tableView = new TableView<>();
// 设置可编辑
tableView.setEditable(true);
// 表格宽度
tableView.setMaxWidth(((StartApplication.SCREEN_WIDTH * 0.8) - StartApplication.LEFT_MENU_WIDTH) * 0.8);
// 设置可选择多行数据
tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
// 解析表头和列信息
TableColumnUtil.parseColumnsByClass(tableView.getColumns(), beanClass);
// 添加表格数据
data = FXCollections.observableArrayList();
data.addAll(dataList);
tableView.setItems(data);
}
public void resetData(List<T> newDataList) {
ObservableList<T> list = FXCollections.observableArrayList();
list.addAll(newDataList);
data = list;
tableView.setItems(list);
}
}
2.5 自定义分页组件
package org.feng.demofx.pages;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import lombok.Data;
import org.feng.demofx.util.ControlUtil;
/**
* 自定义分页组件
*
* @author feng
*/
@Data
public class CustomPagingComponent {
/**
* 每页条数
*/
private ComboBox<String> pageSize;
/**
* 当前页
*/
private TextField currentPage;
/**
* 上一页
*/
private Button lastPageButton;
/**
* 下一页
*/
private Button nextPageButton;
/**
* 跳转
*/
private Button skipPageButton;
/**
* 首页
*/
private Button firstPageButton;
/**
* 最后一页
*/
private Button finallyPageButton;
/**
* 分页面板
*/
private GridPane pagingGridPane;
/**
* 总页数
*/
private Label pageCount;
public CustomPagingComponent(double width, double height) {
this(width, height, null, null);
}
public CustomPagingComponent(double width, double height, Integer pageCount, Integer pageSize) {
pagingGridPane = new GridPane();
pagingGridPane.setMaxWidth(width);
pagingGridPane.setMinWidth(width);
pagingGridPane.setMaxHeight(height);
pagingGridPane.setMinHeight(height);
pagingGridPane.setPadding(new Insets(8, 0, 8, 0));
// 设置下拉框,选择每页条数;默认15条
this.pageSize = new ComboBox<>();
this.pageSize.getItems().addAll("10", "15", "30", "50", "100");
this.pageSize.setValue(pageSize == null ? "15" : String.valueOf(pageSize));
// 当前页默认设置为1
currentPage = new TextField();
currentPage.setText("1");
currentPage.setMaxWidth(35);
// 上一页,下一页
lastPageButton = ControlUtil.pagingButton("上一页");
nextPageButton = ControlUtil.pagingButton("下一页");
skipPageButton = ControlUtil.pagingButton("跳转");
firstPageButton = ControlUtil.pagingButton("首页");
finallyPageButton = ControlUtil.pagingButton("最后一页");
// 总页数
this.pageCount = new Label(String.format(" %s ", pageCount == null ? "0" : String.valueOf(pageCount)));
// 设置垂直间距,水平间距
pagingGridPane.setVgap(5);
pagingGridPane.setHgap(3);
pagingGridPane.add(new Label("总页数:"), 0, 0);
pagingGridPane.add(this.pageCount, 1, 0);
Node spacer1 = new Region();
HBox.setHgrow(spacer1, Priority.ALWAYS);
pagingGridPane.add(spacer1, 2, 0);
pagingGridPane.add(firstPageButton, 3, 0);
pagingGridPane.add(lastPageButton, 4, 0);
pagingGridPane.add(currentPage, 5, 0);
pagingGridPane.add(nextPageButton, 6, 0);
pagingGridPane.add(finallyPageButton, 7, 0);
pagingGridPane.add(skipPageButton, 8, 0);
Node spacer2 = new Region();
HBox.setHgrow(spacer2, Priority.ALWAYS);
pagingGridPane.add(spacer2, 9, 0);
pagingGridPane.add(this.pageSize, 10, 0);
}
public int getCurrentPageValue() {
return Integer.parseInt(this.getCurrentPage().getText());
}
public void setCurrentPageValue(int currentPageValue) {
this.getCurrentPage().setText(String.valueOf(currentPageValue));
}
public int getPageSizeValue() {
return Integer.parseInt(this.getPageSize().getValue());
}
public void setPageSizeValue(int pageSizeValue) {
this.getPageSize().setValue(String.valueOf(pageSizeValue));
}
public void setPageCountValue(int pageCountValue) {
this.getPageCount().setText(String.valueOf(pageCountValue));
}
}
2.6 动物管理页面
package org.feng.demofx.pages;
import cn.hutool.core.bean.BeanUtil;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import lombok.Data;
import org.feng.demofx.StartApplication;
import org.feng.demofx.dao.impl.AnimalDao;
import org.feng.demofx.entity.bo.AnimalBO;
import org.feng.demofx.enums.AnimalSexEnum;
import org.feng.demofx.enums.AnimalTypeEnum;
import org.feng.demofx.service.AnimalService;
import org.feng.demofx.service.impl.AnimalServiceImpl;
import org.feng.demofx.sys.PageNodeKey;
import org.feng.demofx.sys.PageNodeRender;
import org.feng.demofx.util.*;
import org.feng.demofx.vo.AnimalVo;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* 动物管理界面
*
* @author feng
*/
@PageNodeKey(value = AnimalManagePage.PAGE_KEY, pageName = "动物管理")
public class AnimalManagePage implements PageNodeRender {
public static final String PAGE_KEY = "animalManage";
private final AnimalService animalService;
public AnimalManagePage() {
animalService = new AnimalServiceImpl(new AnimalDao());
}
@Override
public Node render(Pane pane) {
VBox vbox = new VBox();
// 当前容器内,布局居中,局上
vbox.setAlignment(Pos.TOP_CENTER);
// 当前面板背景颜色
StyleUtil.setPaneBackground(vbox, Color.web("#fffff0"));
// 页面标题
vbox.getChildren().add(genPageTitle("动物管理页面"));
final double width = ((StartApplication.SCREEN_WIDTH * 0.8) - StartApplication.LEFT_MENU_WIDTH) * 0.8;
// 操作按钮,
VBox oper = new VBox();
HBox hBox = new HBox();
hBox.setMaxWidth(250);
oper.getChildren().add(hBox);
Button addButton = ControlUtil.genAddButton();
Button updateButton = ControlUtil.genUpdateButton();
Button deleteButton = ControlUtil.genDeleteButton();
Button refreshButton = ControlUtil.genRefreshButton();
// 设置外边距,上、右,下,左
oper.setPadding(new Insets(10, 0, 12, 0));
oper.setAlignment(Pos.TOP_LEFT);
oper.setMaxWidth(width);
oper.setMaxHeight(200);
Node spacer1 = new Region();
HBox.setHgrow(spacer1, Priority.ALWAYS);
Node spacer2 = new Region();
HBox.setHgrow(spacer2, Priority.ALWAYS);
Node spacer3 = new Region();
HBox.setHgrow(spacer3, Priority.ALWAYS);
hBox.getChildren().add(addButton);
hBox.getChildren().add(spacer1);
hBox.getChildren().add(updateButton);
hBox.getChildren().add(spacer2);
hBox.getChildren().add(deleteButton);
hBox.getChildren().add(spacer3);
hBox.getChildren().add(refreshButton);
vbox.getChildren().add(oper);
// 搜索面板
SearchAnimalPane searchAnimalPane = new SearchAnimalPane(oper.getMaxWidth());
vbox.getChildren().add(searchAnimalPane.getSearchPane());
// 分页组件
CustomPagingComponent customPagingComponent = new CustomPagingComponent(width, 60);
// 控制台输入操作记录
LogTextAreaComponent logger = new LogTextAreaComponent(400, width);
// 表格视图
PageBean<AnimalVo> pageBean = new PageBean<>(customPagingComponent.getPageSizeValue(),
customPagingComponent.getCurrentPageValue(), 0);
animalService.listPage(searchAnimalPane.genAnimalBo(), pageBean);
customPagingComponent.setPageCountValue(pageBean.getSumPageCount());
CustomTableViewComponent<AnimalVo> tableViewComponent = new CustomTableViewComponent<>(AnimalVo.class, pageBean.getData());
TableView<AnimalVo> animalTableView = tableViewComponent.getTableView();
// 表格展示数据+分页
vbox.getChildren().add(animalTableView);
vbox.getChildren().add(customPagingComponent.getPagingGridPane());
// 分隔线
vbox.getChildren().add(ControlUtil.separator(animalTableView.getMaxWidth()));
// 操作记录
vbox.getChildren().add(logger.getLogBox());
// 注册刷新
refreshButton.setOnAction(event -> {
ControlUtil.refreshTableView(animalTableView, logger);
});
// 更改每页大小-事件处理
customPagingComponent.getPageSize().setOnAction(event -> {
int pageSize = customPagingComponent.getPageSizeValue();
logger.log(String.format("修改每页大小为:%s,重新刷新列表!", pageSize));
// 设置每页大小
pageBean.setPageSize(pageSize);
// 重新分页查询第一页数据
refreshToFirstPage(pageBean, searchAnimalPane, customPagingComponent);
// 刷新列表
tableViewComponent.resetData(pageBean.getData());
ControlUtil.refreshTableView(animalTableView, logger);
});
// 上一页
customPagingComponent.getLastPageButton().setOnAction(event -> {
// 当前页
int currentPage = customPagingComponent.getCurrentPageValue();
String canQuery = pageBean.setCurrentPageNo(currentPage - 1);
if (StringUtil.isNotEmpty(canQuery)) {
logger.log(canQuery);
return;
}
animalService.listPage(searchAnimalPane.genAnimalBo(), pageBean);
customPagingComponent.setPageCountValue(pageBean.getSumPageCount());
customPagingComponent.setCurrentPageValue(currentPage - 1);
// 刷新列表
tableViewComponent.resetData(pageBean.getData());
ControlUtil.refreshTableView(animalTableView, logger);
});
// 下一页
customPagingComponent.getNextPageButton().setOnAction(event -> {
// 当前页
int currentPage = customPagingComponent.getCurrentPageValue();
String canQuery = pageBean.setCurrentPageNo(currentPage + 1);
if (StringUtil.isNotEmpty(canQuery)) {
logger.log(canQuery);
return;
}
animalService.listPage(searchAnimalPane.genAnimalBo(), pageBean);
customPagingComponent.setCurrentPageValue(currentPage + 1);
customPagingComponent.setPageCountValue(pageBean.getSumPageCount());
// 刷新列表
tableViewComponent.resetData(pageBean.getData());
ControlUtil.refreshTableView(animalTableView, logger);
});
// 首页事件
customPagingComponent.getFirstPageButton().setOnAction(event -> {
refreshToFirstPage(pageBean, searchAnimalPane, customPagingComponent);
logger.log("跳转首页成功!");
// 刷新列表
tableViewComponent.resetData(pageBean.getData());
ControlUtil.refreshTableView(animalTableView, logger);
});
// 最后一页
customPagingComponent.getFinallyPageButton().setOnAction(event -> {
pageBean.setCurrentPageNo(pageBean.getSumPageCount());
animalService.listPage(searchAnimalPane.genAnimalBo(), pageBean);
customPagingComponent.setPageCountValue(pageBean.getSumPageCount());
customPagingComponent.setCurrentPageValue(pageBean.getSumPageCount());
// 刷新列表
tableViewComponent.resetData(pageBean.getData());
ControlUtil.refreshTableView(animalTableView, logger);
});
// 跳转事件
customPagingComponent.getSkipPageButton().setOnAction(event -> {
int currentPage = customPagingComponent.getCurrentPageValue();
if(currentPage < 1) {
currentPage = 1;
}
pageBean.setCurrentPageNo(currentPage);
animalService.listPage(searchAnimalPane.genAnimalBo(), pageBean);
// 如果输入的当前页大于总页数,修复跳转至最后一页
if(currentPage > pageBean.getSumPageCount()) {
currentPage = pageBean.getSumPageCount();
pageBean.setCurrentPageNo(currentPage);
animalService.listPage(searchAnimalPane.genAnimalBo(), pageBean);
}
customPagingComponent.setPageCountValue(pageBean.getSumPageCount());
customPagingComponent.setCurrentPageValue(currentPage);
logger.log(String.format("跳转第【%s】页成功!", currentPage));
// 刷新列表
tableViewComponent.resetData(pageBean.getData());
ControlUtil.refreshTableView(animalTableView, logger);
});
// 当选择发生变化时调用该函数-选择某一行时
animalTableView.getSelectionModel().selectedItemProperty().addListener((obs, oldVal, newVal) -> {
if (newVal != null && !animalTableView.getSelectionModel().isSelected(newVal.getId())) {
logger.log("已选择 " + newVal.getName());
}
});
// 注册搜索
searchAnimalPane.getSearchButton().setOnAction(event -> {
refreshToFirstPage(pageBean, searchAnimalPane, customPagingComponent);
logger.log("搜索成功!");
// 刷新列表
tableViewComponent.resetData(pageBean.getData());
ControlUtil.refreshTableView(animalTableView, logger);
});
// 注册删除
deleteButton.setOnAction(ControlUtil.alertConfirm("确认删除吗?", "", buttonType -> {
ButtonBar.ButtonData buttonData = buttonType.getButtonData();
if (buttonData.isDefaultButton()) {
// 确认删除
ObservableList<AnimalVo> items = animalTableView.getSelectionModel().getSelectedItems();
List<Long> ids = items.stream().mapToLong(vo -> Long.valueOf(vo.getId())).boxed().toList();
logger.log("正在删除 " + items.stream().map(AnimalVo::getName).collect(Collectors.joining(",")));
animalService.delete(ids);
// 刷新列表
refreshToFirstPage(pageBean, searchAnimalPane, customPagingComponent);
tableViewComponent.resetData(pageBean.getData());
ControlUtil.refreshTableView(animalTableView, logger);
logger.log("删除 " + items.stream().map(AnimalVo::getName).collect(Collectors.joining(",")) + "成功!");
}
}));
// 注册新增
addButton.setOnAction(event -> {
AddAndUpdateAnimalDialog addDialog = new AddAndUpdateAnimalDialog(null);
addDialog.getDialog().showAndWait().ifPresent(buttonType -> {
if (buttonType.getButtonData().isDefaultButton()) {
logger.log("开始新增数据");
// 获取文本框中数据
AnimalBO animalBO = addDialog.genAnimalBo();
logger.log("正在新增 " + animalBO.getName());
// 添加动物
animalService.add(animalBO);
// 刷新列表
refreshToFirstPage(pageBean, searchAnimalPane, customPagingComponent);
tableViewComponent.resetData(pageBean.getData());
ControlUtil.refreshTableView(animalTableView, logger);
logger.log("新增 " + animalBO.getName() + "成功!");
}
});
addDialog.getDialog().close();
});
// 注册更新
updateButton.setOnAction(event -> {
ObservableList<AnimalVo> selectedItems = animalTableView.getSelectionModel().getSelectedItems();
if (selectedItems == null || selectedItems.size() != 1) {
logger.log("请选择一条数据进行修改");
return;
}
// 获取选中的数据原值
AnimalVo selectedAnimal = selectedItems.get(0);
AddAndUpdateAnimalDialog updateDialog = new AddAndUpdateAnimalDialog(selectedAnimal);
// 注册点击事件
updateDialog.getDialog().showAndWait().ifPresent(buttonType -> {
if (buttonType.getButtonData().isDefaultButton()) {
logger.log("开始修改数据");
// 获取文本框中数据
AnimalBO animalBO = updateDialog.genAnimalBo();
animalBO.setId(selectedAnimal.getId().longValue());
logger.log(String.format("正在修改ID为【%s】,名字为【%s】 ", selectedAnimal.getId(), updateDialog.getOldAnimal().getName()));
// 更新动物
animalService.update(animalBO);
// 刷新列表
refreshToFirstPage(pageBean, searchAnimalPane, customPagingComponent);
tableViewComponent.resetData(pageBean.getData());
ControlUtil.refreshTableView(animalTableView, logger);
logger.log(String.format("修改完成! ID为【%s】,修改名字【%s】为【%s】,动物类型【%s】为【%s】,年龄【%s】为【%s】", selectedAnimal.getId(), updateDialog.getOldAnimal().getName(), animalBO.getName(),
updateDialog.getOldAnimal().getAnimalType(), animalBO.getAnimalType().getDesc(), updateDialog.getOldAnimal().getAge(), animalBO.getAge()));
}
});
updateDialog.getDialog().close();
});
// 动物管理页面初始化完成,刷新一次
ControlUtil.refreshTableView(animalTableView, logger);
return vbox;
}
/**
* 刷新列表数据至第一页
*/
private void refreshToFirstPage(PageBean<AnimalVo> pageBean, SearchAnimalPane searchAnimalPane, CustomPagingComponent customPagingComponent) {
pageBean.setCurrentPageNo(1);
animalService.listPage(searchAnimalPane.genAnimalBo(), pageBean);
customPagingComponent.setPageCountValue(pageBean.getSumPageCount());
customPagingComponent.setCurrentPageValue(1);
}
@Data
private static class SearchAnimalPane {
// 折叠面板
private TitledPane searchPane;
private TextField searchNameTextField;
private TextField searchAgeTextField;
private ComboBox<String> searchTypeComboBox;
private ComboBox<String> searchSexComboBox;
private DatePicker createDateStart;
private DatePicker createDateEnd;
private DatePicker updateDateStart;
private DatePicker updateDateEnd;
private Button searchButton;
public SearchAnimalPane(double width) {
// 定义搜索网格面板
GridPane searchGridPane = new GridPane();
// 设置垂直间距,水平间距
searchGridPane.setVgap(10);
searchGridPane.setHgap(5);
searchNameTextField = new TextField();
searchAgeTextField = new TextField();
searchTypeComboBox = new ComboBox<>();
searchTypeComboBox.getItems().addAll(Arrays.stream(AnimalTypeEnum.values()).map(AnimalTypeEnum::getDesc).toList());
searchSexComboBox = new ComboBox<>();
searchSexComboBox.getItems().addAll(Arrays.stream(AnimalSexEnum.values()).map(AnimalSexEnum::getDesc).toList());
searchButton = ControlUtil.genSearchButton();
Button clearButton = ControlUtil.genClearButton();
createDateStart = new DatePicker();
createDateEnd = new DatePicker();
updateDateStart = new DatePicker();
updateDateEnd = new DatePicker();
createDateStart.setEditable(true);
createDateEnd.setEditable(true);
updateDateStart.setEditable(true);
updateDateEnd.setEditable(true);
searchGridPane.add(new Label("名字 "), 0, 0);
searchGridPane.add(searchNameTextField, 1, 0);
searchGridPane.add(new Label("年龄 "), 0, 1);
searchGridPane.add(searchAgeTextField, 1, 1);
searchGridPane.add(new Label("类型 "), 0, 2);
searchGridPane.add(searchTypeComboBox, 1, 2);
searchGridPane.add(new Label("性别 "), 0, 3);
searchGridPane.add(searchSexComboBox, 1, 3);
searchGridPane.add(new Label("创建日期起止 "), 0, 4);
searchGridPane.add(new Label("更新日期起止 "), 0, 5);
searchGridPane.add(createDateStart, 1, 4);
searchGridPane.add(createDateEnd, 2, 4);
searchGridPane.add(updateDateStart, 1, 5);
searchGridPane.add(updateDateEnd, 2, 5);
searchGridPane.add(clearButton, 0, 7);
searchGridPane.add(searchButton, 1, 7);
// 折叠面板-搜索
searchPane = new TitledPane("高级搜索", searchGridPane);
// 设置外边距,上、右,下,左
searchPane.setPadding(new Insets(0, 0, 8, 0));
searchPane.setMaxWidth(width);
// 设置可折叠
searchPane.setCollapsible(true);
// 设置折叠时的动画
searchPane.setAnimated(true);
// 注册清空参数事件
clearButton.setOnAction(event -> {
clearSearchParam();
});
}
public void clearSearchParam() {
searchNameTextField.setText("");
searchAgeTextField.setText("");
searchTypeComboBox.setValue("");
searchSexComboBox.setValue("");
createDateStart.setValue(null);
createDateEnd.setValue(null);
updateDateStart.setValue(null);
updateDateEnd.setValue(null);
}
public AnimalBO genAnimalBo() {
String searchNameText = this.getSearchNameTextField().getText();
String searchAgeText = this.getSearchAgeTextField().getText();
String searchTypeValue = this.getSearchTypeComboBox().getValue();
String searchSexValue = this.getSearchSexComboBox().getValue();
LocalDate createDateStartValue = this.getCreateDateStart().getValue();
LocalDate createDateEndValue = this.getCreateDateEnd().getValue();
LocalDate updateDateStartValue = this.getUpdateDateStart().getValue();
LocalDate updateDateEndValue = this.getUpdateDateEnd().getValue();
AnimalVo animalVo = new AnimalVo();
if (StringUtil.isNotEmpty(searchNameText)) {
animalVo.setName(searchNameText);
}
if (StringUtil.isNotEmpty(searchAgeText)) {
animalVo.setAge(Integer.parseInt(searchAgeText));
}
if (StringUtil.isNotEmpty(searchTypeValue)) {
animalVo.setAnimalType(searchTypeValue);
}
if (StringUtil.isNotEmpty(searchSexValue)) {
animalVo.setAnimalSex(searchSexValue);
}
// 创建时间起、止选择后生效
if (Objects.nonNull(createDateStartValue) || Objects.nonNull(createDateEndValue)) {
animalVo.setCreateDateRange(new DateRange(createDateStartValue, createDateEndValue));
}
// 更新时间起、止选择后生效
if (Objects.nonNull(updateDateStartValue) || Objects.nonNull(updateDateEndValue)) {
animalVo.setUpdateDateRange(new DateRange(updateDateStartValue, updateDateEndValue));
}
return animalVo.toAnimalBo();
}
}
@Data
private static class AddAndUpdateAnimalDialog {
private Dialog<ButtonType> dialog;
private TextField nameTextField;
private TextField ageTextField;
private ComboBox<String> typeComboBox;
private ComboBox<String> sexComboBox;
private TextArea remarkTextArea;
private AnimalVo oldAnimal;
public AddAndUpdateAnimalDialog(AnimalVo animalVo) {
Label nameLabel = new Label("名字 ");
nameTextField = new TextField();
Label ageLabel = new Label("年龄 ");
ageTextField = new TextField();
Label typeLabel = new Label("类型 ");
typeComboBox = new ComboBox<>();
typeComboBox.getItems().addAll(Arrays.stream(AnimalTypeEnum.values()).map(AnimalTypeEnum::getDesc).toList());
Label sexLabel = new Label("性别 ");
sexComboBox = new ComboBox<>();
sexComboBox.getItems().addAll(Arrays.stream(AnimalSexEnum.values()).map(AnimalSexEnum::getDesc).toList());
Label remarkLabel = new Label("备注 ");
remarkTextArea = new TextArea();
remarkTextArea.setScrollTop(Double.MAX_VALUE);
remarkTextArea.setEditable(true);
remarkTextArea.setMaxHeight(250);
remarkTextArea.setMaxWidth(300);
if (animalVo == null) {
typeComboBox.setValue(AnimalTypeEnum.UN_KNOW.getDesc());
sexComboBox.setValue(AnimalSexEnum.UN_KNOW.getDesc());
} else {
oldAnimal = new AnimalVo();
BeanUtil.copyProperties(animalVo, oldAnimal);
// 数据回显
nameTextField.setText(oldAnimal.getName());
ageTextField.setText(String.valueOf(oldAnimal.getAge()));
typeComboBox.setValue(oldAnimal.getAnimalType());
sexComboBox.setValue(oldAnimal.getAnimalSex());
remarkTextArea.setText(oldAnimal.getRemark());
}
// 定义网格面板
GridPane gridPane = new GridPane();
// 设置垂直间距,水平间距
gridPane.setVgap(10);
gridPane.setHgap(5);
gridPane.add(nameLabel, 0, 0);
gridPane.add(nameTextField, 1, 0);
gridPane.add(ageLabel, 0, 1);
gridPane.add(ageTextField, 1, 1);
gridPane.add(typeLabel, 0, 2);
gridPane.add(typeComboBox, 1, 2);
gridPane.add(sexLabel, 0, 3);
gridPane.add(sexComboBox, 1, 3);
gridPane.add(remarkLabel, 0, 4);
gridPane.add(remarkTextArea, 1, 4);
DialogPane dialogPane = new DialogPane();
dialogPane.setPrefHeight(300);
dialogPane.setPrefWidth(500);
dialogPane.setMaxHeight(400);
dialogPane.setMaxWidth(550);
dialogPane.setContent(gridPane);
dialogPane.getButtonTypes().addAll(ButtonType.CANCEL, ButtonType.OK);
dialog = new Dialog<>();
dialog.setDialogPane(dialogPane);
}
public AnimalBO genAnimalBo() {
AnimalBO animalBO = new AnimalBO();
// 年龄
animalBO.setAge(Integer.parseInt(this.getAgeTextField().getText()));
// 姓名
animalBO.setName(this.getNameTextField().getText());
// 获取下拉框的数据:动物类型
String type = this.getTypeComboBox().getValue();
animalBO.setAnimalType(AnimalTypeEnum.getByDesc(type));
// 获取下拉框的数据:性别
String sex = this.getSexComboBox().getValue();
animalBO.setAnimalSex(AnimalSexEnum.getByDesc(sex));
// 获取文本域内容:备注
animalBO.setRemark(this.getRemarkTextArea().getText());
return animalBO;
}
}
}
2.7 页面工厂
package org.feng.demofx.sys;
import org.feng.demofx.util.StringUtil;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
/**
* PageNodeRender工厂
*
* @author feng
*/
public class PageNodeRenderFactory {
private static final Map<String, PageNodeRender> PAGE_NODE_RENDER_MAP = new HashMap<>(8);
private static final Set<PageNodeDefine> PAGE_NODE_DEFINES = new TreeSet<>(Comparator.comparingInt(PageNodeDefine::getOrder)
.thenComparing(PageNodeDefine::getPageKey));
private static final Map<String, String> PAGE_NAME_MAP = new LinkedHashMap<>();
private static final Map<String, String> RESERVE_PAGE_NAME_MAP = new LinkedHashMap<>();
public static PageNodeRender newInstance(String pageKey) {
return PAGE_NODE_RENDER_MAP.get(pageKey);
}
public static Map<String, String> getPageNameMap() {
return PAGE_NAME_MAP;
}
public static String getPageKeyByName(String pageName) {
return RESERVE_PAGE_NAME_MAP.get(pageName);
}
public static void registerNodeRender(Class<? extends PageNodeRender> renderClass) {
try {
// 处理PageNodeRender#getPageKey()
PageNodeRender pageNodeRender = renderClass.getDeclaredConstructor().newInstance();
String pageKey = pageNodeRender.getPageKey();
if (StringUtil.isNotEmpty(pageKey)) {
PAGE_NODE_RENDER_MAP.put(pageKey, pageNodeRender);
PageNodeDefine pageNodeDefine = new PageNodeDefine();
pageNodeDefine.setPageKey(pageKey);
pageNodeDefine.setPageName(pageNodeRender.getPageName());
pageNodeDefine.setOrder(pageNodeRender.order());
PAGE_NODE_DEFINES.add(pageNodeDefine);
return;
}
// 处理PageNodeKey注解
if (renderClass.isAnnotationPresent(PageNodeKey.class)) {
PageNodeKey pageNodeKey = renderClass.getAnnotation(PageNodeKey.class);
pageKey = pageNodeKey.value();
if (StringUtil.isNotEmpty(pageKey)) {
PAGE_NODE_RENDER_MAP.put(pageKey, pageNodeRender);
PageNodeDefine pageNodeDefine = new PageNodeDefine();
pageNodeDefine.setPageKey(pageKey);
pageNodeDefine.setPageName(pageNodeKey.pageName());
pageNodeDefine.setOrder(pageNodeKey.order());
PAGE_NODE_DEFINES.add(pageNodeDefine);
return;
}
}
throw new IllegalArgumentException(renderClass.getName() + "定义错误,请检查");
} catch (NoSuchMethodException | InvocationTargetException | InstantiationException |
IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public static void fillPageNameMap() {
PAGE_NODE_DEFINES.forEach(pageNodeDefine -> {
PAGE_NAME_MAP.put(pageNodeDefine.getPageKey(), pageNodeDefine.getPageName());
RESERVE_PAGE_NAME_MAP.put(pageNodeDefine.getPageName(), pageNodeDefine.getPageKey());
});
}
}
附录
附1:sql脚本
CREATE TABLE `fx_animal`
(
`id` BIGINT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL COMMENT '名字',
`animal_type` INT NOT NULL DEFAULT - 1 COMMENT '动物类型,-1 未知',
`animal_sex` INT NOT NULL DEFAULT - 1 COMMENT '动物性别,-1 未知 0 雄性 1雌性',
`age` INT NOT NULL DEFAULT 0 COMMENT '年龄',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`remark` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '备注',
PRIMARY KEY (`id`)
);
INSERT INTO `fx_demo`.`fx_animal`(`id`, `name`, `animal_type`, `animal_sex`, `age`, `update_time`, `create_time`, `remark`) VALUES (1, '小名', 0, 1, 2, '2024-01-19 23:10:36', '2024-01-19 22:31:53', '呜呜呜汪汪汪');
INSERT INTO `fx_demo`.`fx_animal`(`id`, `name`, `animal_type`, `animal_sex`, `age`, `update_time`, `create_time`, `remark`) VALUES (2, '小红', 1, 0, 0, '2024-01-19 23:50:17', '2024-01-19 22:31:59', '00000');
INSERT INTO `fx_demo`.`fx_animal`(`id`, `name`, `animal_type`, `animal_sex`, `age`, `update_time`, `create_time`, `remark`) VALUES (3, '啊华', 1, 1, 2, '2024-01-19 23:13:46', '2024-01-19 23:13:46', '喵喵喵');
INSERT INTO `fx_demo`.`fx_animal`(`id`, `name`, `animal_type`, `animal_sex`, `age`, `update_time`, `create_time`, `remark`) VALUES (4, '小吗', 1, 1, 2, '2024-01-22 23:34:09', '2024-01-22 23:34:09', '');
INSERT INTO `fx_demo`.`fx_animal`(`id`, `name`, `animal_type`, `animal_sex`, `age`, `update_time`, `create_time`, `remark`) VALUES (5, '待机', -1, -1, 2, '2024-01-22 23:34:56', '2024-01-22 23:34:56', '');
INSERT INTO `fx_demo`.`fx_animal`(`id`, `name`, `animal_type`, `animal_sex`, `age`, `update_time`, `create_time`, `remark`) VALUES (6, '松紧带哦哦', -1, -1, 21, '2024-01-22 23:35:05', '2024-01-22 23:35:05', '');
INSERT INTO `fx_demo`.`fx_animal`(`id`, `name`, `animal_type`, `animal_sex`, `age`, `update_time`, `create_time`, `remark`) VALUES (7, '额温枪', -1, -1, 21, '2024-01-22 23:35:10', '2024-01-22 23:35:10', '');
INSERT INTO `fx_demo`.`fx_animal`(`id`, `name`, `animal_type`, `animal_sex`, `age`, `update_time`, `create_time`, `remark`) VALUES (8, '大萨达', -1, -1, 2, '2024-01-22 23:35:16', '2024-01-22 23:35:16', '');
INSERT INTO `fx_demo`.`fx_animal`(`id`, `name`, `animal_type`, `animal_sex`, `age`, `update_time`, `create_time`, `remark`) VALUES (9, '哇打算', -1, -1, 21, '2024-01-22 23:35:21', '2024-01-22 23:35:21', '');
INSERT INTO `fx_demo`.`fx_animal`(`id`, `name`, `animal_type`, `animal_sex`, `age`, `update_time`, `create_time`, `remark`) VALUES (10, '大东方闪电', -1, -1, 22, '2024-01-22 23:35:37', '2024-01-22 23:35:37', '');
INSERT INTO `fx_demo`.`fx_animal`(`id`, `name`, `animal_type`, `animal_sex`, `age`, `update_time`, `create_time`, `remark`) VALUES (11, '2多发点', -1, -1, 4322, '2024-01-22 23:35:50', '2024-01-22 23:35:50', '');
INSERT INTO `fx_demo`.`fx_animal`(`id`, `name`, `animal_type`, `animal_sex`, `age`, `update_time`, `create_time`, `remark`) VALUES (12, '绕弯儿', -1, -1, 32, '2024-01-22 23:37:23', '2024-01-22 23:37:23', '');
INSERT INTO `fx_demo`.`fx_animal`(`id`, `name`, `animal_type`, `animal_sex`, `age`, `update_time`, `create_time`, `remark`) VALUES (13, '法国掉头发', -1, -1, 32, '2024-01-22 23:37:30', '2024-01-22 23:37:30', '');
INSERT INTO `fx_demo`.`fx_animal`(`id`, `name`, `animal_type`, `animal_sex`, `age`, `update_time`, `create_time`, `remark`) VALUES (14, '43给对方刚刚', -1, -1, 32, '2024-01-22 23:37:36', '2024-01-22 23:37:36', '');
INSERT INTO `fx_demo`.`fx_animal`(`id`, `name`, `animal_type`, `animal_sex`, `age`, `update_time`, `create_time`, `remark`) VALUES (15, '手动挡', -1, -1, 2, '2024-01-22 23:37:45', '2024-01-22 23:37:45', '');
INSERT INTO `fx_demo`.`fx_animal`(`id`, `name`, `animal_type`, `animal_sex`, `age`, `update_time`, `create_time`, `remark`) VALUES (16, '发得分', -1, -1, 32, '2024-01-22 23:37:53', '2024-01-22 23:37:53', '');
INSERT INTO `fx_demo`.`fx_animal`(`id`, `name`, `animal_type`, `animal_sex`, `age`, `update_time`, `create_time`, `remark`) VALUES (17, '明明', 1, 1, 13, '2024-01-24 21:03:58', '2024-01-24 21:03:21', '修改了一次年龄');