一個完善的數據查詢界面,是不能沒有分頁的
效果:
框架: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; }