org.bklab.crud.FluentCrudView Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of fluent-vaadin-flow Show documentation
Show all versions of fluent-vaadin-flow Show documentation
Broderick Labs for fluent vaadin flow. Inherits common Vaadin components.
package org.bklab.crud;
import com.vaadin.flow.component.AttachEvent;
import com.vaadin.flow.component.HasValue;
import com.vaadin.flow.component.dependency.CssImport;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.grid.GridSortOrder;
import com.vaadin.flow.component.grid.contextmenu.GridContextMenu;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.treegrid.TreeGrid;
import com.vaadin.flow.data.event.SortEvent;
import com.vaadin.flow.data.provider.DataProvider;
import com.vaadin.flow.data.provider.ListDataProvider;
import com.vaadin.flow.data.provider.hierarchy.HierarchicalDataProvider;
import com.vaadin.flow.data.provider.hierarchy.TreeData;
import com.vaadin.flow.data.provider.hierarchy.TreeDataProvider;
import com.vaadin.flow.router.BeforeEnterEvent;
import com.vaadin.flow.router.BeforeEnterObserver;
import com.vaadin.flow.shared.Registration;
import org.bklab.crud.core.ICrudViewExcelExportSupporter;
import org.bklab.crud.core.IFluentCrudViewCommonField;
import org.bklab.crud.menu.FluentCrudMenuButton;
import org.bklab.crud.menu.ICrudViewMenuColumnSupporter;
import org.bklab.crud.menu.IFluentGridMenuBuilder;
import org.bklab.flow.base.HasReturnThis;
import org.bklab.flow.components.button.FluentButton;
import org.bklab.flow.components.pagination.PageSwitchEvent;
import org.bklab.flow.components.pagination.Pagination;
import org.bklab.flow.components.pagination.layout.MiddleCustomPaginationLayout;
import org.bklab.flow.components.textfield.KeywordField;
import org.bklab.flow.dialog.ErrorDialog;
import org.bklab.flow.layout.EmptyLayout;
import org.bklab.flow.layout.ToolBar;
import org.bklab.flow.util.url.QueryParameterUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.function.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@SuppressWarnings("UnusedReturnValue")
@CssImport("./styles/org/bklab/component/crud/fluent-crud-view.css")
public abstract class FluentCrudView> extends VerticalLayout implements
IFluentCrudViewCommonField>,
ICrudViewMenuColumnSupporter>,
ICrudViewExcelExportSupporter>,
HasReturnThis>,
BeforeEnterObserver {
protected final List> effectQueryParameterUtils = new ArrayList<>();
private final static String CLASS_NAME = "fluent-crud-view";
private static final Logger logger = LoggerFactory.getLogger(FluentCrudView.class);
protected final Map> parameterMap = new LinkedHashMap<>();
protected final ToolBar header = new ToolBar();
protected final Div content = new Div();
protected final Div footer = new Div();
protected final List entities = new ArrayList<>();
protected final List inMemoryFilteredEntities = new ArrayList<>();
protected final Map> inMemoryEntityFilter = new LinkedHashMap<>();
protected final Pagination pagination = new Pagination().onePageSize(20).limit(10).customLayout(new MiddleCustomPaginationLayout());
protected final List>> afterReloadListeners = new ArrayList<>();
private final List> exceptionConsumers = new ArrayList<>();
private final List> menuButtons = new ArrayList<>();
protected final EmptyLayout emptyLayout = new EmptyLayout("暂无数据");
protected FluentButton searchButton = (FluentButton) new FluentButton(VaadinIcon.SEARCH, "查询")
.primary().asFactory().clickListener(e -> reloadGridData()).get();
protected boolean hasGridMenu = true;
protected boolean hasPagination = true;
protected Function> childProvider = null;
protected Function, DataProvider> dataProviderCreator = null;
protected BiPredicate sameEntityBiPredicate = Objects::equals;
protected final G grid;
protected QueryParameterUtil queryParameterUtil;
public FluentCrudView() {
emptyLayout.setVisible(false);
getStyle().set("padding", "0.5em 0 0").set("background-color", "white");
addClassName(CLASS_NAME);
header.addClassName(CLASS_NAME + "__header");
content.addClassName(CLASS_NAME + "__content");
footer.addClassName(CLASS_NAME + "__footer");
emptyLayout.addClassName(CLASS_NAME + "__empty");
add(header, content, footer);
emptyLayout.getElement().getStyle().set("flex-grow", "1");
emptyLayout.setHeight("calc(100% - 5em)");
addExceptionConsumer(e -> new ErrorDialog(e).header("错误").build().open());
addExceptionConsumer(e -> logger.error(Optional.ofNullable(e.getLocalizedMessage()).orElse("错误"), e));
beforeInitGrid();
this.grid = createGrid();
grid.addClassName(CLASS_NAME + "__grid");
}
protected void beforeInitGrid() {
}
public FluentCrudView useRefreshIconButton() {
searchButton.reset().noPadding().link().iconOnly().asFactory().tooltip("刷新").icon(VaadinIcon.REFRESH.create()).text(null);
return this;
}
@Deprecated
public FluentCrudView addRefreshIconButton() {
return useRefreshIconButton();
}
public FluentCrudView addExceptionConsumer(Consumer exceptionConsumer) {
this.exceptionConsumers.add(exceptionConsumer);
return this;
}
public Consumer getExceptionConsumer() {
return exception -> exceptionConsumers.forEach(a -> a.accept(exception));
}
public abstract G createGrid();
public abstract Collection queryEntities(Map parameters);
public FluentCrudView headerVisible(boolean visible) {
header.setVisible(visible);
if (visible) content.removeClassName("fluent-crud-view__no_header");
else content.addClassName("fluent-crud-view__no_header");
return this;
}
public FluentCrudView watch(HasValue, ?> hasValue) {
Registration registration = hasValue.addValueChangeListener(e -> reloadGridData());
addDetachListener(e -> registration.remove());
return this;
}
public FluentCrudView noPagination() {
this.hasPagination = false;
return this;
}
public FluentCrudView hasPagination() {
this.hasPagination = true;
return this;
}
public FluentCrudView toggleEmpty(boolean empty) {
return empty ? toggleEmpty() : toggleNotEmpty();
}
private FluentCrudView toggleEmpty() {
if (grid != null) {
if (!grid.getFooterRows().isEmpty()) {
return toggleNotEmpty();
}
grid.getStyle().remove("height");
grid.getStyle().remove("min-height");
grid.setHeightByRows(true);
grid.setHeight("unset");
if (grid instanceof TreeGrid) {
grid.setItems(new TreeDataProvider<>(new TreeData<>()));
} else {
grid.setItems(new ListDataProvider<>(Collections.emptyList()));
}
}
footer.setVisible(false);
emptyLayout.setVisible(true);
return this;
}
private FluentCrudView toggleNotEmpty() {
grid.setHeightByRows(false);
grid.setHeightFull();
emptyLayout.setVisible(false);
footer.setVisible(true);
return this;
}
public BiPredicate getSameEntityBiPredicate() {
if (sameEntityBiPredicate == null) this.sameEntityBiPredicate = Objects::equals;
return sameEntityBiPredicate;
}
public FluentCrudView setSameEntityBiPredicate(BiPredicate sameEntityBiPredicate) {
this.sameEntityBiPredicate = Objects.requireNonNull(sameEntityBiPredicate, "sameEntityBiPredicate is null");
return this;
}
public FluentCrudView menuColumnAtFirst() {
Grid.Column menuColumn = grid.getColumnByKey("menuColumn");
if (menuColumn == null) return this;
List> columns = new ArrayList<>(grid.getColumns());
columns.remove(menuColumn);
columns.add(0, menuColumn);
grid.setColumnOrder(columns);
return this;
}
public FluentCrudView reloadContextMenu(T entity, Predicate samePredicate) {
for (FluentCrudMenuButton menuButton : menuButtons) {
menuButton.reload(entity, samePredicate != null
? samePredicate : t -> getSameEntityBiPredicate().test(entity, t));
}
return this;
}
public FluentCrudView removeContextMenu(T entity) {
return this.removeContextMenu(entity, sameEntityBiPredicate);
}
public FluentCrudView removeContextMenu(T entity, BiPredicate sameEntityBiPredicate) {
menuButtons.stream().filter(m -> m.isThisEntity(entity, sameEntityBiPredicate))
.collect(Collectors.toList()).forEach(menuButtons::remove);
return this;
}
private void handleGridGridSortOrderSortEvent(SortEvent, GridSortOrder> gridGridSortOrderSortEvent) {
List filteredEntities = inMemoryFilteredEntities.stream().sorted((o1, o2) -> {
for (GridSortOrder order : gridGridSortOrderSortEvent.getSortOrder()) {
Grid.Column sorted = order.getSorted();
int compare = sorted.getComparator(order.getDirection()).compare(o1, o2);
if (compare == 0) continue;
return compare;
}
return 0;
}).collect(Collectors.toList());
inMemoryFilteredEntities.clear();
inMemoryFilteredEntities.addAll(filteredEntities);
pagination.refresh();
}
@Override
public void beforeEnter(BeforeEnterEvent beforeEnterEvent) {
this.queryParameterUtil = new QueryParameterUtil(beforeEnterEvent);
}
@Override
protected void onAttach(AttachEvent attachEvent) {
super.onAttach(attachEvent);
header.right(searchButton);
content.remove(grid, emptyLayout);
content.add(grid, emptyLayout);
if (hasPagination) {
footer.add(pagination);
} else {
footer.setVisible(false);
content.getStyle().set("border-bottom", "none");
}
pagination.pageSwitchEvent(createPageSwitchEvent());
grid.addSortListener(this::handleGridGridSortOrderSortEvent);
this.effectQueryParameterUtils.forEach(a -> a.accept(queryParameterUtil));
reloadGridData();
onAfterAttach();
}
protected void onAfterAttach() {
}
public void insertEntity(T entity) {
this.entities.add(entity);
this.inMemoryFilteredEntities.add(entity);
this.pagination.totalData(this.entities.size()).build();
toggleNotEmpty();
}
public void deleteEntity(T entity) {
this.entities.remove(entity);
this.inMemoryFilteredEntities.remove(entity);
this.pagination.totalData(this.entities.size()).build();
this.removeContextMenu(entity);
toggleEmpty(inMemoryFilteredEntities.isEmpty());
}
public void deleteEntity(T entity, BiPredicate isEquals) {
List instance = this.entities.stream().filter(t -> isEquals.test(t, entity)).collect(Collectors.toList());
this.entities.removeAll(instance);
this.inMemoryFilteredEntities.removeAll(entities);
this.pagination.totalData(this.entities.size()).build();
this.removeContextMenu(entity, isEquals == null ? getSameEntityBiPredicate() : isEquals);
toggleEmpty(inMemoryFilteredEntities.isEmpty());
}
public void updateEntity(T entity) {
this.updateEntity(entity, t -> getSameEntityBiPredicate().test(t, entity));
}
public void updateEntity(T oldEntity, T newEntity) {
this.updateEntity(newEntity, t -> getSameEntityBiPredicate().test(oldEntity, t));
}
public void updateEntity(T entity, Predicate isEquals) {
replaceSameEntity(entities, entity, isEquals);
replaceSameEntity(inMemoryFilteredEntities, entity, isEquals);
this.reloadContextMenu(entity, isEquals);
this.pagination.totalData(this.entities.size()).build();
}
private void replaceSameEntity(Collection collection, T entity, Predicate isEquals) {
List instances = collection.stream().map(t -> isEquals.test(t) ? entity : t).collect(Collectors.toList());
collection.clear();
collection.addAll(instances);
}
public FluentCrudView buildGridMenu(IFluentGridMenuBuilder menuEntityBiConsumer) {
GridContextMenu gridContextMenu = grid.addContextMenu();
gridContextMenu.setDynamicContentHandler(entity -> {
if (entity == null) {
return false;
}
gridContextMenu.removeAll();
grid.select(entity);
menuEntityBiConsumer.safeBuild(this, gridContextMenu, entity);
return true;
});
return this;
}
protected PageSwitchEvent createPageSwitchEvent() {
return (currentPageNumber, pageSize, lastPageNumber, isFromClient) -> setGridItem(
hasPagination ? this.inMemoryFilteredEntities.stream().skip((long) (Math.max(Math.min(currentPageNumber, this.inMemoryFilteredEntities.size()
/ pageSize + 1), 1) - 1) * pageSize).limit(pageSize).collect(Collectors.toList()) : inMemoryFilteredEntities
);
}
public FluentCrudView setGridItem(Collection items) {
if (dataProviderCreator != null) {
grid.setItems(dataProviderCreator.apply(items));
} else if (grid instanceof TreeGrid) {
//noinspection unchecked,rawtypes,rawtypes
((TreeGrid>) grid).setDataProvider((HierarchicalDataProvider) new TreeDataProvider<>((new TreeData()).addItems(items, getChildProvider()::apply)));
} else {
grid.setItems(items);
}
return this;
}
protected Function> getChildProvider() {
return childProvider == null ? a -> Collections.emptyList() : childProvider;
}
public FluentCrudView addParameter(String name, HasValue, ?> hasValue) {
return addParameter(name, hasValue, s -> s instanceof String ? ((String) s).trim().isEmpty() ? null : ((String) s).trim() : s);
}
public FluentCrudView addParameter(String name, HasValue, V> hasValue, Function mapValue) {
parameterMap.put(name, () -> Optional.ofNullable(hasValue.getValue()).map(mapValue).orElse(null));
return this;
}
protected KeywordField addKeywordField() {
KeywordField keywordField = new KeywordField((c, v) -> reloadGridData());
parameterMap.put("keyword", () -> Optional.ofNullable(keywordField.getValue()).map(k -> k.trim().isEmpty() ? null : k.trim()).orElse(null));
return keywordField;
}
protected FluentCrudView addKeywordFieldToLeft() {
header.left(addKeywordField());
return this;
}
protected FluentCrudView addKeywordFieldToMiddle() {
header.middle(addKeywordField());
return this;
}
protected FluentCrudView addKeywordFieldToRight() {
header.right(addKeywordField());
return this;
}
public void reloadGridDataInMemory() {
List entities = this.entities.stream().filter(t ->
inMemoryEntityFilter.values().stream().filter(Objects::nonNull).allMatch(p -> p.test(t))
).collect(Collectors.toList());
this.inMemoryFilteredEntities.clear();
this.inMemoryFilteredEntities.addAll(entities);
pagination.totalData(entities.size()).build();
toggleEmpty(entities.isEmpty());
afterReloadListeners.forEach(a -> {
try {
a.accept(entities);
} catch (Exception e) {
logger.warn("afterReloadListeners throws an error.", e);
}
});
grid.recalculateColumnWidths();
}
public void reloadGridData() {
this.entities.clear();
Map map = new LinkedHashMap<>();
parameterMap.forEach((k, v) -> Optional.ofNullable(v).map(Supplier::get).ifPresent(v1 -> map.put(k, v1)));
Optional.ofNullable(queryEntities(map)).ifPresent(this.entities::addAll);
this.reloadGridDataInMemory();
}
public void switchEntityPage(Predicate predicate) {
IntStream.range(0, inMemoryFilteredEntities.size()).filter(i -> predicate.test(inMemoryFilteredEntities.get(i)))
.findFirst().ifPresent(i -> pagination.setCurrentPage(i / pagination.getPageSize() + 1).refresh());
}
@Override
public ToolBar header() {
return header;
}
@Override
public Collection getEntities() {
return entities;
}
@Override
public G getGrid() {
return grid;
}
@Override
public List> getMenuButtons() {
return menuButtons;
}
@Override
public FluentCrudView getCrudView() {
return this;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy