一个完善的数据查询界面,是不能没有分页的
效果:
框架:SpringBoot+JavaFx+Hibernate
fxml:
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.geometry.*?> <?import javafx.scene.control.*?> <?import java.lang.*?> <?import javafx.scene.layout.*?> <BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.maxinhai.world.controller.DynamicTableViewController"> <top> <HBox prefHeight="42.0" prefWidth="800.0" BorderPane.alignment="CENTER"> <children> <Button mnemonicParsing="false" text="首页" onAction="#toIndex"> <HBox.margin> <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> </HBox.margin> </Button> <Button mnemonicParsing="false" text="新增"> <HBox.margin> <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> </HBox.margin> </Button> <Button mnemonicParsing="false" text="查询"> <HBox.margin> <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> </HBox.margin> </Button> <Button mnemonicParsing="false" text="修改"> <HBox.margin> <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> </HBox.margin> </Button> <Button mnemonicParsing="false" text="删除"> <HBox.margin> <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> </HBox.margin> </Button> </children> </HBox> </top> <center> <VBox prefHeight="200.0" prefWidth="100.0" BorderPane.alignment="CENTER"> <children> <HBox prefHeight="43.0" prefWidth="800.0"> <children> <Label id="codeLabel" fx:id="codeLabel" alignment="CENTER_RIGHT" contentDisplay="RIGHT" prefWidth="75.0" text="编码:" textAlignment="CENTER"> <HBox.margin> <Insets bottom="10.0" left="10.0" top="10.0" /> </HBox.margin> </Label> <TextField id="codeText" prefWidth="180.0" fx:id="coedTextField"> <HBox.margin> <Insets bottom="5.0" right="5.0" top="5.0" /> </HBox.margin> </TextField> <Label id="nameLabel" fx:id="nameLabel" alignment="CENTER_RIGHT" contentDisplay="RIGHT" prefWidth="75.0" text="名称:" textAlignment="CENTER"> <HBox.margin> <Insets bottom="10.0" left="20.0" top="10.0" /> </HBox.margin> </Label> <TextField id="codeText" prefWidth="180.0" fx:id="nameTextField"> <HBox.margin> <Insets bottom="5.0" right="5.0" top="5.0" /> </HBox.margin> </TextField> </children> </HBox> <HBox prefHeight="42.0" prefWidth="800.0"> <children> <Label text="开始时间:"> <HBox.margin> <Insets bottom="10.0" left="10.0" top="10.0" /> </HBox.margin> </Label> <DatePicker prefWidth="180.0" fx:id="beginDatePicker"> <HBox.margin> <Insets bottom="5.0" right="5.0" top="5.0" /> </HBox.margin> </DatePicker> <Label text="结束时间:"> <HBox.margin> <Insets bottom="10.0" left="20.0" top="10.0" /> </HBox.margin> </Label> <DatePicker prefWidth="180.0" fx:id="endDatePicker"> <HBox.margin> <Insets bottom="5.0" right="5.0" top="5.0" /> </HBox.margin> </DatePicker> </children> </HBox> <HBox prefHeight="473.0" prefWidth="800.0" fx:id="parent"> <children> <!--实现分页插件与表格控件结合的关键在于两者要在同一个父容器下--> <TableView prefHeight="100.0" prefWidth="802.0" fx:id="tableView" /> <Pagination fx:id="pagination" pageCount="10" prefHeight="35.0" prefWidth="800.0" BorderPane.alignment="CENTER" /> </children> </HBox> </children> </VBox> </center> <bottom> <!--错误位置--> <!--<Pagination fx:id="pagination" pageCount="10" prefHeight="35.0" prefWidth="800.0" BorderPane.alignment="CENTER" />--> </bottom> </BorderPane>
controller:
import com.maxinhai.world.WorldApplication; import com.maxinhai.world.entity.ClientClockInRecord; import com.maxinhai.world.repository.ClientClockInRepository; import com.maxinhai.world.service.impl.DynamicTableViewServiceImpl; import com.maxinhai.world.utils.ModuleUtils; import de.felixroske.jfxsupport.FXMLController; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.Node; import javafx.scene.control.*; import javafx.scene.layout.HBox; import javafx.util.Callback; import org.springframework.beans.factory.annotation.Autowired; import java.net.URL; import java.util.*; /** * @program: world * @description: 动态表格控制器 * @author: XinHai.Ma * @create: 2021-05-24 14:33 */ @FXMLController public class DynamicTableViewController extends BaseController implements Initializable { @FXML private TextField coedTextField; @FXML private TextField nameTextField; @FXML private Label codeLabel; @FXML private Label nameLabel; @FXML private DatePicker beginDatePicker; @FXML private DatePicker endDatePicker; @FXML private TableView<ClientClockInRecord> tableView; @FXML private HBox parent; @FXML private Pagination pagination; // 删除集合 private List<String> deleteList = new ArrayList<>(); // 编辑集合 private List<String> updateList = new ArrayList<>(); @Autowired private ClientClockInRepository clockInRepository; @Autowired private DynamicTableViewServiceImpl dynamicTableViewService; @Override public void initialize(URL location, ResourceBundle resources) { // 初始化表格 LinkedHashMap<String, String> columns = new LinkedHashMap<>(); columns.put("gid", "主键"); columns.put("clientId", "员工id"); columns.put("clientName", "员工名称"); columns.put("workStatus", "状态"); columns.put("createBy", "创建者"); columns.put("createTime", "创建时间"); columns.put("editBy", "编辑者"); columns.put("editTime", "编辑时间"); List<TableColumn> columnList = ModuleUtils.createColumn(true, columns, deleteList, updateList); HashMap<String, Object> params = new HashMap<>(); params.put("pageIndex", 1); params.put("pageSize", 28); Map<String, Object> result = dynamicTableViewService.select(params); List<ClientClockInRecord> dataList = (List<ClientClockInRecord>)result.get("data"); //clockInRepository.findAll(); ModuleUtils.createDynamicTable(parent, tableView, columnList, dataList); // 初始化分页插件 pagination.setCurrentPageIndex(0); pagination.setPageCount(Integer.valueOf(result.get("total").toString())); pagination.setPageFactory(new Callback<Integer, Node>() { @Override public Node call(Integer pageIndex) { return createPage(pageIndex); } }); // 分页插件宽度(不设置分页插件宽度表格宽度也无法随窗口变化) WorldApplication.getScene().widthProperty().addListener(new ChangeListener<Number>() { @Override public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) { pagination.setPrefWidth(newValue.doubleValue()); } }); } /** * 页面切换调用该方法 * @param pageIndex * @return */ private TableView<ClientClockInRecord> createPage(int pageIndex) { HashMap<String, Object> params = new HashMap<>(); params.put("pageIndex", pageIndex); params.put("pageSize", 28); Map<String, Object> result = dynamicTableViewService.select(params); List<ClientClockInRecord> dataList = (List<ClientClockInRecord>)result.get("data"); ObservableList<ClientClockInRecord> items = FXCollections.observableArrayList(dataList); tableView.setItems(items); return tableView; } @FXML public void select () { HashMap<String, Object> params = new HashMap<>(); Map<String, Object> result = dynamicTableViewService.select(params); List<ClientClockInRecord> dataList = (List<ClientClockInRecord>)result.get("data"); } }
service:
import com.maxinhai.world.entity.ClientClockInRecord; import com.maxinhai.world.repository.ClientClockInRepository; import com.maxinhai.world.service.DynamicTableViewService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; /** * @program: world * @description: 动态表格业务层 * @author: XinHai.Ma * @create: 2021-05-24 15:03 */ @SuppressWarnings("all") @Service public class DynamicTableViewServiceImpl implements DynamicTableViewService { @Autowired private ClientClockInRepository clockInRepository; @Override public Map<String, Object> select(Map<String, Object> params) { String pageIndex = params.get("pageIndex").toString(); String pageSize = params.get("pageSize").toString(); PageRequest page = PageRequest.of(Integer.valueOf(pageIndex), Integer.valueOf(pageSize), Sort.Direction.DESC, "createTime"); Page<ClientClockInRecord> recordPage = clockInRepository.findAll(new Specification<ClientClockInRecord>() { @Override public Predicate toPredicate(Root<ClientClockInRecord> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { List<Predicate> condition = new ArrayList<Predicate>(); condition.add(criteriaBuilder.equal(root.get("isDelete").as(Integer.class), 0)); condition.add(criteriaBuilder.equal(root.get("isActive").as(Integer.class), 0)); // 编号 if (Objects.nonNull(params.get("code"))) { condition.add(criteriaBuilder.equal(root.get("code").as(String.class), params.get("code").toString())); } // 名称 if (Objects.nonNull(params.get("name"))) { condition.add(criteriaBuilder.equal(root.get("name").as(String.class), params.get("name").toString())); } // 开始、结束时间 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); if (Objects.nonNull(params.get("beginTime")) && Objects.nonNull(params.get("endTime"))) { // 开始时间和结束时间之内 String beginTime = params.get("beginTime").toString() + " 00:00:00"; String endTime = params.get("endTime").toString() + " 23:59:59"; condition.add(criteriaBuilder.between(root.<LocalDateTime>get("createTime"), LocalDateTime.parse(beginTime, formatter), LocalDateTime.parse(endTime, formatter))); } else if (Objects.nonNull(params.get("beginTime"))) { // 大于开始时间 String beginTime = params.get("beginTime").toString() + " 00:00:00"; condition.add(criteriaBuilder.greaterThanOrEqualTo(root.<LocalDateTime>get("createTime"), LocalDateTime.parse(beginTime, formatter))); } else if (Objects.nonNull(params.get("endTime"))) { // 小于结束时间 String beginTime = params.get("endTime").toString() + " 23:59:59"; condition.add(criteriaBuilder.lessThanOrEqualTo(root.<LocalDateTime>get("createTime"), LocalDateTime.parse(beginTime, formatter))); } Predicate[] p = new Predicate[condition.size()]; return criteriaBuilder.and(condition.toArray(p)); } }, page); Map<String, Object> result = new HashMap<>(); result.put("data", recordPage.get().collect(Collectors.toList())); result.put("total", recordPage.getTotalPages()); return result; } }
dao:
import com.maxinhai.world.entity.ClientClockInRecord; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import java.util.List; @Repository public interface ClientClockInRepository extends JpaRepository<ClientClockInRecord, String>, JpaSpecificationExecutor<ClientClockInRecord> { }
组件初始化代码:
/** * 方法描述:根据columnList(表头集合)、dataList(数据集合)初始化tableView * 注意:设置TableView的宽高去适应界面的大小行不通,只能设置父元素的大小,让TableView自适应 * * @param parentNode 父容器 * @param tableView tableView对象 * @param columnList 表格字段集合 * @param dataList 表格数据集合 */ public static void createDynamicTable(Pane parentNode, TableView tableView, List<TableColumn> columnList, List dataList) { AssertUtils.assertTrue(Objects.isNull(tableView), "Component is Null!"); // 设置表格可编辑 tableView.setEditable(true); // 设置多选 tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); // 去掉空白多于列 tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); // 设置表头 tableView.getColumns().addAll(columnList); // 更新表格数据 ObservableList<ClientLoginRecord> data = FXCollections.observableArrayList(dataList); tableView.setItems(data); // 使tableView随窗口变化而变化 WorldApplication.getScene().heightProperty().addListener(new ChangeListener<Number>() { @Override public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) { parentNode.setPrefHeight(newValue.doubleValue()); } }); WorldApplication.getScene().widthProperty().addListener(new ChangeListener<Number>() { @Override public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) { tableView.setPrefWidth(newValue.doubleValue()); } }); } /** * 根据isCheckBox、columns创建表格表头 * * @param isCheckBox 是否创建多选列 * @param columns bean字段对应中文集合 * @return */ public static List<TableColumn> createColumn(boolean isCheckBox, LinkedHashMap<String, String> columns, List<String> delList, List<String> updList) { List<TableColumn> columnList = new ArrayList<>(); // 多选框 if (isCheckBox) { TableColumn<BaseEntity, CheckBox> checkCol = new TableColumn("单选框"); checkCol.setMinWidth(30); checkCol.setMinWidth(30); checkCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<BaseEntity, CheckBox>, ObservableValue<CheckBox>>() { @Override public ObservableValue<CheckBox> call(TableColumn.CellDataFeatures<BaseEntity, CheckBox> param) { CheckBox checkBox = new CheckBox(); // 设置checkBox居中,貌似没用 checkBox.setAlignment(Pos.CENTER); checkBox.setOnAction(event -> { boolean selected = checkBox.isSelected(); String gid = param.getValue().getGid(); if (selected) { delList.add(gid); updList.removeAll(updList); updList.add(gid); System.out.println("选中: " + gid); } else { delList.remove(gid); updList.remove(gid); System.out.println("取消选中: " + gid); } }); return new ReadOnlyObjectWrapper<CheckBox>(checkBox); } }); columnList.add(checkCol); } // 序号列 TableColumn seqCol = new TableColumn("序号"); seqCol.setMinWidth(20); seqCol.setMinWidth(20); seqCol.setCellFactory(new IDCell<>()); columnList.add(seqCol); Map<String, Double> defaultWidthMap = getDefaultWidthMap(); // 创建其他表头 columns.forEach((k, v) -> { TableColumn column = new TableColumn(v); if ("gid".equals(k)) { column.setVisible(false); } if (getDateColSet().contains(k)) { formatDateTimeCol(column); } if (Objects.isNull(defaultWidthMap.get(k))) { // 没有默认长度,设置100宽度 column.setMinWidth(100); } else { column.setMinWidth(defaultWidthMap.get(k)); } column.setCellValueFactory(new PropertyValueFactory<ClientLoginRecord, String>(k)); columnList.add(column); }); return columnList; } /** * 格式化时间字段(Java8) * * @param column */ public static void formatDateTimeCol(TableColumn column) { DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); column.setCellFactory(data -> { TableCell<Object, LocalDateTime> cell = new TableCell<Object, LocalDateTime>() { @Override protected void updateItem(LocalDateTime item, boolean empty) { super.updateItem(item, empty); if (empty) { setText(null); } else { if (item != null) this.setText(format.format(item)); } } }; return cell; }); } /** * 格式化时间字段(Java7) * * @param column */ public static void formatDateCol(TableColumn column) { SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); column.setCellFactory(data -> { TableCell<Object, Date> cell = new TableCell<Object, Date>() { @Override protected void updateItem(Date item, boolean empty) { super.updateItem(item, empty); if (empty) { setText(null); } else { if (item != null) this.setText(format.format(item)); } } }; return cell; }); } /** * 获取时间字段集合 * * @return */ public static Set<String> getDateColSet() { Set<String> colSet = new HashSet<>(); colSet.add("createTime"); colSet.add("editTime"); return colSet; } /** * 刷新tableView * * @param tableView * @param dataList */ public static void refresh(TableView tableView, List dataList) { ObservableList items = tableView.getItems(); items.removeAll(items); items.addAll(dataList); tableView.refresh(); } /** * 获取默认字段对应长度集合 * * @return */ public static Map<String, Double> getDefaultWidthMap() { Map<String, Double> map = new HashMap<>(); map.put("gid", 350.00); map.put("account", 100.00); map.put("code", 100.00); map.put("description", 100.00); map.put("remark", 100.00); map.put("createBy", 100.00); map.put("createTime", 200.00); map.put("editBy", 100.00); map.put("editTime", 200.00); map.put("username", 200.00); map.put("password", 200.00); return map; }