SUPPORT-8975: add custom grid
This commit is contained in:
parent
e5c7e003a0
commit
1697d8302b
21 changed files with 1013 additions and 4 deletions
|
|
@ -0,0 +1,14 @@
|
|||
package ru.micord.ervu.account_applications.component.exception;
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
public class GridException extends RuntimeException{
|
||||
public GridException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public GridException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package ru.micord.ervu.account_applications.component.field.persist.filter;
|
||||
|
||||
import ru.cg.webbpm.modules.standard_annotations.validation.NotNull;
|
||||
import ru.cg.webbpm.modules.webkit.beans.Behavior;
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
|
||||
public class StaticFilterComponent extends Behavior {
|
||||
@NotNull
|
||||
public String name;
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package ru.micord.ervu.account_applications.component.model;
|
||||
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class LoadServiceResponse<T> {
|
||||
private Set<T> data;
|
||||
private int page;
|
||||
private int perPage;
|
||||
private int totalRows;
|
||||
|
||||
public Set<T> getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(Set<T> data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public int getPage() {
|
||||
return page;
|
||||
}
|
||||
|
||||
public void setPage(int page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
public int getPerPage() {
|
||||
return perPage;
|
||||
}
|
||||
|
||||
public void setPerPage(int perPage) {
|
||||
this.perPage = perPage;
|
||||
}
|
||||
|
||||
public int getTotalRows() {
|
||||
return totalRows;
|
||||
}
|
||||
|
||||
public void setTotalRows(int totalRows) {
|
||||
this.totalRows = totalRows;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package ru.micord.ervu.account_applications.component.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class Role {
|
||||
private String id;
|
||||
private String name;
|
||||
private String shortname;
|
||||
private String displayName;
|
||||
private String description;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getShortname() {
|
||||
return shortname;
|
||||
}
|
||||
|
||||
public void setShortname(String shortname) {
|
||||
this.shortname = shortname;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public void setDisplayName(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
package ru.micord.ervu.account_applications.component.model;
|
||||
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class User {
|
||||
private String accountId;
|
||||
private String accountFullName;
|
||||
private String domainName;
|
||||
private boolean enabled;
|
||||
private boolean blocked;
|
||||
private List<Role> roles;
|
||||
|
||||
|
||||
public List<Role> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public void setRoles(List<Role> roles) {
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
public String getAccountId() {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
public void setAccountId(String accountId) {
|
||||
this.accountId = accountId;
|
||||
}
|
||||
|
||||
public String getAccountFullName() {
|
||||
return accountFullName;
|
||||
}
|
||||
|
||||
public void setAccountFullName(String accountFullName) {
|
||||
this.accountFullName = accountFullName;
|
||||
}
|
||||
|
||||
public String getDomainName() {
|
||||
return domainName;
|
||||
}
|
||||
|
||||
public void setDomainName(String domainName) {
|
||||
this.domainName = domainName;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public boolean isBlocked() {
|
||||
return blocked;
|
||||
}
|
||||
|
||||
public void setBlocked(boolean blocked) {
|
||||
this.blocked = blocked;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
User user = (User) o;
|
||||
return enabled == user.enabled && blocked == user.blocked && Objects.equals(
|
||||
accountId, user.accountId) && Objects.equals(accountFullName,
|
||||
user.accountFullName
|
||||
) && Objects.equals(domainName, user.domainName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = Objects.hashCode(accountId);
|
||||
result = 31 * result + Objects.hashCode(accountFullName);
|
||||
result = 31 * result + Objects.hashCode(domainName);
|
||||
result = 31 * result + Boolean.hashCode(enabled);
|
||||
result = 31 * result + Boolean.hashCode(blocked);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package ru.micord.ervu.account_applications.component.property.grid;
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
public enum FilterType {
|
||||
TEXT,
|
||||
DATE,
|
||||
NUMBER,
|
||||
SET
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package ru.micord.ervu.account_applications.component.property.grid;
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
public class StaticColumn {
|
||||
public String column;
|
||||
public String type;
|
||||
public FilterType filterType;
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
package ru.micord.ervu.account_applications.component.property.grid;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import component.field.dataconvert.DataConverter;
|
||||
import component.field.dataconvert.DataConverterProvider;
|
||||
import component.field.persist.filter.AbstractFilterComponent;
|
||||
import component.grid.model.PinnedType;
|
||||
import property.grid.Column;
|
||||
import property.grid.Formatter;
|
||||
import utils.GridUtils;
|
||||
|
||||
import ru.cg.webbpm.modules.database.bean.AggregationFunction;
|
||||
import ru.cg.webbpm.modules.database.bean.entity_graph.EntityColumn;
|
||||
import ru.cg.webbpm.modules.standard_annotations.editor.Visible;
|
||||
import ru.cg.webbpm.modules.webkit.annotations.Exclude;
|
||||
import ru.cg.webbpm.modules.webkit.annotations.Model;
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
@Model
|
||||
public class StaticGridColumn extends AbstractFilterComponent<Object, Object>
|
||||
implements Column {
|
||||
|
||||
public StaticColumn field;
|
||||
public String displayName;
|
||||
public String headerTooltip;
|
||||
@Visible(predicate = "displayPopup != true")
|
||||
public String columnTooltip;
|
||||
public Integer width;
|
||||
public boolean widthFixed;
|
||||
public boolean autoHeight;
|
||||
public boolean hidden;
|
||||
public boolean sortable;
|
||||
public boolean disableHiding;
|
||||
public PinnedType pinned;
|
||||
public boolean filter;
|
||||
@Exclude
|
||||
public Formatter<?, ?> formatter;
|
||||
public AggregationFunction aggregationFunction;
|
||||
@Exclude
|
||||
public Formatter<?, ?> aggregationFormatter;
|
||||
@Exclude
|
||||
public Formatter<?, ?> exportFileFormatter;
|
||||
public boolean displayPopup;
|
||||
public boolean suppressHeaderMenu;
|
||||
|
||||
public EntityColumn getField() {
|
||||
return GridUtils.toEntityColumn(field.column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public Collection<EntityColumn> getEntityColumns() {
|
||||
return Collections.singleton(getField());
|
||||
}
|
||||
|
||||
public AggregationFunction getAggregationFunction() {
|
||||
return this.aggregationFunction;
|
||||
}
|
||||
|
||||
public Formatter<?, ?> getFormatter() {
|
||||
return this.formatter;
|
||||
}
|
||||
|
||||
public Formatter<?, ?> getAggregationFormatter() {
|
||||
return this.aggregationFormatter;
|
||||
}
|
||||
|
||||
public Formatter<?, ?> getExportFormatter() {
|
||||
return exportFileFormatter;
|
||||
}
|
||||
|
||||
public Object convertData(Object rawValue) {
|
||||
if (this.dataConverter != null) {
|
||||
return this.dataConverter.convertValueForSave(rawValue);
|
||||
}
|
||||
else {
|
||||
DataConverter converter = DataConverterProvider.getDataConverter(rawValue.getClass());
|
||||
return converter.convertValueForSave(rawValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
package ru.micord.ervu.account_applications.component.service;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import model.FileModel;
|
||||
import model.Filter;
|
||||
import model.grid.GridDataExportFormat;
|
||||
import model.grid.GridRow;
|
||||
import model.grid.GridRows;
|
||||
import model.grid.SortInfo;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
import ru.micord.ervu.account_applications.component.exception.GridException;
|
||||
import ru.micord.ervu.account_applications.component.field.persist.filter.StaticFilterComponent;
|
||||
import ru.micord.ervu.account_applications.component.model.LoadServiceResponse;
|
||||
import ru.micord.ervu.account_applications.component.model.Role;
|
||||
import ru.micord.ervu.account_applications.component.model.User;
|
||||
import ru.micord.ervu.account_applications.security.context.SecurityContext;
|
||||
import ru.micord.ervu.account_applications.util.StringUtils;
|
||||
import service.GridService;
|
||||
|
||||
import ru.cg.webbpm.modules.webkit.beans.Behavior;
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
@Service
|
||||
public class ErvuUserGridLoadService extends Behavior implements GridService {
|
||||
private static final String FILTER_PATH = "/service/idm/accounts/search/filter/v1";
|
||||
private final ObjectMapper objectMapper;
|
||||
private final SecurityContext securityContext;
|
||||
@Value("${ervu.http.timeout:10}")
|
||||
private int httpTimeout;
|
||||
@Value("${ervu.url:https://ervu-dev.pgs.rtlabs.ru}")
|
||||
private String ervuUrl;
|
||||
|
||||
public ErvuUserGridLoadService(ObjectMapper objectMapper,
|
||||
SecurityContext securityContext) {
|
||||
this.objectMapper = objectMapper;
|
||||
this.securityContext = securityContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GridRows loadData(Integer offset, Integer limit, Filter[] filters,
|
||||
SortInfo[] sortInfos) {
|
||||
LoadServiceResponse<User> loadServiceResponse = loadDataFromApi(offset, limit, filters);
|
||||
List<GridRow> gridRows = convertToGridRows(loadServiceResponse.getData());
|
||||
return new GridRows(gridRows);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GridRows loadDataWithRowCount(Integer offset, Integer limit, Filter[] filters,
|
||||
SortInfo[] sortInfos) {
|
||||
LoadServiceResponse<User> loadServiceResponse = loadDataFromApi(offset, limit, filters);
|
||||
List<GridRow> gridRows = convertToGridRows(loadServiceResponse.getData());
|
||||
return new GridRows(gridRows, loadServiceResponse.getTotalRows());
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileModel exportData(String[] columnIdsToExport, GridDataExportFormat exportFormat,
|
||||
Filter[] filters, SortInfo[] sortInfo) {
|
||||
return GridService.super.exportData(columnIdsToExport, exportFormat, filters, sortInfo);
|
||||
}
|
||||
|
||||
private LoadServiceResponse<User> loadDataFromApi(Integer offset, Integer limit,
|
||||
Filter[] filters) {
|
||||
try {
|
||||
Map<String, Object> requestBodyMap = createRequestBody(offset, limit, filters);
|
||||
String requestBody = objectMapper.writeValueAsString(requestBodyMap);
|
||||
|
||||
HttpClient httpClient = HttpClient.newBuilder()
|
||||
.connectTimeout(Duration.ofSeconds(httpTimeout))
|
||||
.build();
|
||||
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(UriComponentsBuilder.fromHttpUrl(ervuUrl)
|
||||
.path(FILTER_PATH)
|
||||
.build().toUri())
|
||||
.header(HttpHeaders.CONTENT_TYPE, "application/json")
|
||||
.header(HttpHeaders.AUTHORIZATION, "Bearer " + securityContext.getToken())
|
||||
.timeout(Duration.ofSeconds(httpTimeout))
|
||||
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
|
||||
.build();
|
||||
|
||||
HttpResponse<String> response = httpClient.send(request,
|
||||
HttpResponse.BodyHandlers.ofString()
|
||||
);
|
||||
return objectMapper.readValue(response.body(),
|
||||
new TypeReference<LoadServiceResponse<User>>() {
|
||||
}
|
||||
);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new GridException("Ошибка при загрузке данных", e);
|
||||
}
|
||||
}
|
||||
|
||||
private List<GridRow> convertToGridRows(Set<User> users) {
|
||||
List<GridRow> gridRows = new ArrayList<>();
|
||||
int rowIndex = 0;
|
||||
|
||||
for (User user : users) {
|
||||
GridRow gridRow = new GridRow();
|
||||
gridRow.put("row_uid", rowIndex++);
|
||||
|
||||
for (Field field : User.class.getDeclaredFields()) {
|
||||
processField(field, user, gridRow);
|
||||
}
|
||||
|
||||
gridRows.add(gridRow);
|
||||
}
|
||||
|
||||
return gridRows;
|
||||
}
|
||||
|
||||
private Map<String, Object> createRequestBody(Integer offset, Integer limit, Filter[] filters) {
|
||||
int page = (offset / limit) + 1;
|
||||
Map<String, Object> filterMap = new HashMap<>();
|
||||
filterMap.put("domainId", securityContext.getDomainId());
|
||||
|
||||
if (filters != null && filters.length > 0) {
|
||||
filterMap.putAll(Arrays.stream(filters)
|
||||
.filter(filter -> filter.getFilterModels() != null && filter.getFilterModels().length > 0)
|
||||
.collect(Collectors.toMap(
|
||||
filter -> getScriptInObject(filter.componentGuid, StaticFilterComponent.class).name,
|
||||
filter -> filter.getFilterModels()[0].value,
|
||||
(existing, replacement) -> replacement
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
return Map.of(
|
||||
"searchFilter", filterMap,
|
||||
"page", page,
|
||||
"perPage", limit
|
||||
);
|
||||
}
|
||||
|
||||
private void processField(Field field, User user, GridRow gridRow) {
|
||||
field.setAccessible(true);
|
||||
try {
|
||||
Object value = field.get(user);
|
||||
|
||||
if ("accountFullName".equals(field.getName()) && value instanceof String string) {
|
||||
gridRow.putAll(StringUtils.splitFio(string));
|
||||
return;
|
||||
}
|
||||
if (value instanceof List<?> list && !list.isEmpty() && list.get(0) instanceof Role) {
|
||||
value = list.stream()
|
||||
.map(role -> ((Role) role).getId())
|
||||
.collect(Collectors.joining(", "));
|
||||
}
|
||||
gridRow.put(field.getName(), value != null ? value : "");
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
throw new GridException("Ошибка при получении значения поля: " + field.getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,8 @@ public interface SecurityContext {
|
|||
|
||||
String getUserId();
|
||||
|
||||
String getToken();
|
||||
|
||||
UserSession getUserSession();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,12 @@ public class SecurityContextImpl
|
|||
return auth != null ? auth.getUserSession().userId() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToken() {
|
||||
JwtTokenAuthentication auth = (JwtTokenAuthentication) SecurityContextHolder.getContext().getAuthentication();
|
||||
return auth != null ? (String) auth.getCredentials() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserSession getUserSession() {
|
||||
JwtTokenAuthentication auth = (JwtTokenAuthentication) SecurityContextHolder.getContext().getAuthentication();
|
||||
|
|
|
|||
|
|
@ -9,9 +9,11 @@ import ru.micord.ervu.account_applications.security.model.jwt.UserSession;
|
|||
public class JwtTokenAuthentication implements Authentication {
|
||||
|
||||
private final UserSession userSession;
|
||||
private final String token;
|
||||
|
||||
public JwtTokenAuthentication(UserSession userSession) {
|
||||
public JwtTokenAuthentication(UserSession userSession, String token) {
|
||||
this.userSession = userSession;
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public UserSession getUserSession() {
|
||||
|
|
@ -25,7 +27,7 @@ public class JwtTokenAuthentication implements Authentication {
|
|||
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return null;
|
||||
return token;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ public class ErvuJwtAuthenticationProvider implements AuthenticationProvider {
|
|||
JwtTokenDummy jwtTokenDummy = (JwtTokenDummy) authentication;
|
||||
String jwtToken = jwtTokenDummy.getToken();
|
||||
UserSession userSession = jwtTokenService.getUserSession(jwtToken);
|
||||
return new JwtTokenAuthentication(userSession);
|
||||
return new JwtTokenAuthentication(userSession, jwtToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
package ru.micord.ervu.account_applications.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Adel Kalimullin
|
||||
*/
|
||||
public final class StringUtils {
|
||||
|
||||
private StringUtils() {
|
||||
}
|
||||
|
||||
public static Map<String, String> splitFio(String fio) {
|
||||
String[] parts = fio.trim().split(" ", 3);
|
||||
Map<String, String> result = new HashMap<>();
|
||||
result.put("lastName", parts.length > 0 ? parts[0] : "");
|
||||
result.put("firstName", parts.length > 1 ? parts[1] : "");
|
||||
result.put("middleName", parts.length > 2 ? parts[2] : "");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
<div class="grid"
|
||||
[ngbTooltip]="tooltip | emptyIfNull">
|
||||
|
||||
<ag-grid-angular [ngClass]="theme"
|
||||
[ngStyle]="style"
|
||||
[columnDefs]="columnDefs"
|
||||
[defaultColDef]="defaultColDef"
|
||||
[headerHeight]="headerHeight"
|
||||
[rowHeight]="rowHeight"
|
||||
[rowSelection]="rowSelection"
|
||||
[rowMultiSelectWithClick]="isRowMultiSelectWithClick()"
|
||||
[suppressRowClickSelection]="isSuppressRowClickSelection()"
|
||||
[suppressLoadingOverlay]="isSuppressLoadingOverlay()"
|
||||
[suppressNoRowsOverlay]="isSuppressNoRowsOverlay()"
|
||||
[rowClassRules]="rowClassRules"
|
||||
[rowModelType]="getRowModelType()"
|
||||
[datasource]="datasource"
|
||||
[maxConcurrentDatasourceRequests]="maxConcurrentDatasourceRequests"
|
||||
[blockLoadDebounceMillis]="blockLoadDebounceMillis"
|
||||
[cacheBlockSize]="getBlockSize()"
|
||||
[pagination]="pagination"
|
||||
[paginationPageSize]="fetchSize"
|
||||
[maxBlocksInCache]="0"
|
||||
[getRowId]="getRowIdFunc()"
|
||||
[isRowSelectable]="isRowSelectableFunc()"
|
||||
[pinnedBottomRowData]="pinnedBottomRowData"
|
||||
[suppressDragLeaveHidesColumns]="true"
|
||||
[suppressCopyRowsToClipboard]="true"
|
||||
[processCellForClipboard]="processCellForClipboard"
|
||||
[allowContextMenuWithControlKey]="allowContextMenuWithControlKey"
|
||||
[localeText]="localeText"
|
||||
[enableCellTextSelection]="enableCellTextSelection"
|
||||
[overlayLoadingTemplate]="getLoadingOverlayTemplate()"
|
||||
[overlayNoRowsTemplate]="getNoRowsOverlayTemplate()"
|
||||
[tooltipShowDelay]="tooltipDelay"
|
||||
[accentedSort]="true"
|
||||
(gridReady)="onGridReady($event)"
|
||||
(cellClicked)="onCellClicked($event)"
|
||||
(rowClicked)="onRowClicked($event)"
|
||||
(rowDoubleClicked)="onRowDoubleClicked($event)"
|
||||
(selectionChanged)="onSelectionChanged($event)"
|
||||
(sortChanged)="onSortChanged($event)"
|
||||
(bodyScroll)="onBodyScroll($event)"
|
||||
(columnResized)="onColumnResized($event)"
|
||||
(columnMoved)="onColumnMoved($event)"
|
||||
(columnVisible)="onColumnVisibilityChanged($event)"
|
||||
(filterChanged)="columnFilterChanged($event)"
|
||||
(componentStateChanged)="componentStateChanged($event)">
|
||||
</ag-grid-angular>
|
||||
<div [hidden]="true">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
import {
|
||||
FilterType
|
||||
} from "../../../generated/ru/micord/ervu/account_applications/component/property/grid/FilterType";
|
||||
import {DateFilter, NumberFilter, TextFilter} from "ag-grid-community";
|
||||
import {SetFilter} from "./SetFilter";
|
||||
|
||||
export class CustomGridColumnFilterUtils {
|
||||
|
||||
public static columnFilter(type: FilterType) {
|
||||
if (!type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case FilterType.NUMBER:
|
||||
return NumberFilter;
|
||||
case FilterType.DATE:
|
||||
return DateFilter;
|
||||
case FilterType.SET:
|
||||
return SetFilter;
|
||||
case FilterType.TEXT:
|
||||
default:
|
||||
return TextFilter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
import {ChangeDetectionStrategy, Component} from "@angular/core";
|
||||
import {Event, GridRpcService, GridV2, GridV2Column, Visible} from "@webbpm/base-package";
|
||||
import {
|
||||
StaticGridColumn
|
||||
} from "../../../generated/ru/micord/ervu/account_applications/component/property/grid/StaticGridColumn";
|
||||
import {
|
||||
ColDef,
|
||||
FilterChangedEvent,
|
||||
ICellRendererParams,
|
||||
ITooltipParams,
|
||||
ValueFormatterParams,
|
||||
ValueGetterParams
|
||||
} from "ag-grid-community";
|
||||
import {StaticColumnInitializer} from "./StaticColumnInitializer";
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: 'ervu-static-grid',
|
||||
templateUrl: './../../../../../src/resources/template/account_applications/component/grid/ErvuStaticGrid.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ErvuStaticGrid extends GridV2 {
|
||||
// todo: remove on updating platform version up to 3.188
|
||||
@Visible("false")
|
||||
public columnFiltersChanged: Event<any> = new Event<any>();
|
||||
private rpcService: GridRpcService;
|
||||
|
||||
protected initGrid() {
|
||||
super.initGrid();
|
||||
this.rpcService = this.getScript(GridRpcService);
|
||||
}
|
||||
|
||||
getColumns(): any[] {
|
||||
return this.getScriptsInChildren(GridV2Column)
|
||||
.map(columnV2 => columnV2.getScript(StaticGridColumn));
|
||||
}
|
||||
|
||||
protected columnV2ToColumnDef(column: GridV2Column): ColDef {
|
||||
let gridColumn = column.getScript(StaticGridColumn);
|
||||
let colDef = this.columnToColumnDef(gridColumn);
|
||||
if (column.renderer) {
|
||||
colDef.cellRenderer = (params: ICellRendererParams) => column.renderer.render(params);
|
||||
}
|
||||
if (column.valueGetter) {
|
||||
colDef.valueGetter = (params: ValueGetterParams) => column.valueGetter.get(params);
|
||||
}
|
||||
if (column.tooltipValueGetter) {
|
||||
if (gridColumn.columnTooltip) {
|
||||
throw new Error(
|
||||
"Only one type of tooltip should be specified: tooltipValueGetter or columnTooltip");
|
||||
}
|
||||
colDef.tooltipValueGetter = (params: ITooltipParams) => column.tooltipValueGetter.get(params);
|
||||
}
|
||||
|
||||
column.setGridRef(this);
|
||||
column.setColDef(colDef);
|
||||
return colDef;
|
||||
}
|
||||
|
||||
protected columnToColumnDef(column: any): ColDef {
|
||||
let colDef = StaticColumnInitializer.columnToColumnDef(this, column);
|
||||
let columnComp = column.context;
|
||||
colDef['columnUid'] = columnComp.getObjectId();
|
||||
|
||||
if (columnComp.cellCssClassRulesProvider) {
|
||||
colDef.cellClassRules = columnComp.cellCssClassRulesProvider.getRules();
|
||||
}
|
||||
|
||||
if (columnComp.valueFormatter) {
|
||||
colDef.valueFormatter = (params?: ValueFormatterParams) => {
|
||||
return columnComp.valueFormatter.format(params);
|
||||
}
|
||||
}
|
||||
console.log("Создан ColDef:", colDef);
|
||||
return colDef;
|
||||
}
|
||||
|
||||
@Visible()
|
||||
public getRowDataSize(): number {
|
||||
return this.rowData ? this.rowData.length : 0;
|
||||
}
|
||||
|
||||
// todo: remove on updating platform version up to 3.188
|
||||
@Visible()
|
||||
public hasColumnFilters(): boolean {
|
||||
const filterModel: { [key: string]: any; } = this.gridApi.getFilterModel();
|
||||
return !!filterModel && Object.keys(filterModel).length > 0;
|
||||
}
|
||||
|
||||
// todo: remove on updating platform version up to 3.188
|
||||
public columnFilterChanged(event: FilterChangedEvent) {
|
||||
this.columnFiltersChanged.trigger(event);
|
||||
super.columnFilterChanged(event);
|
||||
}
|
||||
}
|
||||
116
frontend/src/ts/account_applications/component/grid/SetFilter.ts
Normal file
116
frontend/src/ts/account_applications/component/grid/SetFilter.ts
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
import {AgPromise, IDoesFilterPassParams, IFilterComp, IFilterParams} from "ag-grid-community";
|
||||
|
||||
export class SetFilter implements IFilterComp {
|
||||
|
||||
private OPTION_TEMPLATE = `<label class="ag-set-filter-item">
|
||||
<input type="checkbox" class="ag-filter-checkbox" checked/>
|
||||
<span class="ag-filter-value"></span>
|
||||
</label>`;
|
||||
|
||||
private eGui!: HTMLDivElement;
|
||||
private selectAll: HTMLInputElement;
|
||||
private checkboxes: HTMLInputElement[] = [];
|
||||
private values: any[] = [];
|
||||
private initialValues: any[] = [];
|
||||
private filterActive: boolean;
|
||||
private filterChangedCallback!: (additionalEventAttributes?: any) => void;
|
||||
private filterParams!: IFilterParams;
|
||||
private valueType: string;
|
||||
|
||||
init(params: IFilterParams): void {
|
||||
this.eGui = document.createElement('div');
|
||||
this.eGui.className = 'ag-set-filter';
|
||||
let index = 0;
|
||||
this.selectAll = this.initCheckBox('selectAll', 'Все', index);
|
||||
this.checkboxes.push(this.selectAll);
|
||||
|
||||
params.api.getRenderedNodes()
|
||||
.map(node => node.data[params.colDef.field])
|
||||
.sort((n1, n2) => n1 > n2 ? 1 : n1 < n2 ? -1 : 0)
|
||||
.forEach(value => {
|
||||
if (this.values.includes(value)) {
|
||||
return;
|
||||
}
|
||||
index++;
|
||||
let id = `option-${index}`;
|
||||
let checkbox = this.initCheckBox(id, value, index);
|
||||
this.checkboxes.push(checkbox);
|
||||
this.values.push(value);
|
||||
});
|
||||
this.initialValues = this.values.slice();
|
||||
this.filterParams = params;
|
||||
this.filterActive = false;
|
||||
this.filterChangedCallback = params.filterChangedCallback;
|
||||
|
||||
if (this.values.length > 0) {
|
||||
this.valueType = typeof this.values[0];
|
||||
}
|
||||
};
|
||||
|
||||
private initCheckBox(id: string, value: string, index: number): HTMLInputElement {
|
||||
this.eGui.insertAdjacentHTML('beforeend', this.OPTION_TEMPLATE);
|
||||
this.eGui.querySelectorAll('.ag-filter-value')[index].innerHTML = value;
|
||||
let checkbox = this.eGui.querySelectorAll('.ag-filter-checkbox')[index] as HTMLInputElement;
|
||||
checkbox.setAttribute('id', id);
|
||||
checkbox.addEventListener('change', this.onCheckBoxChanged.bind(this));
|
||||
return checkbox;
|
||||
}
|
||||
|
||||
getGui(): HTMLDivElement {
|
||||
return this.eGui;
|
||||
};
|
||||
|
||||
onCheckBoxChanged(event: any) {
|
||||
let checked = event.target.checked;
|
||||
|
||||
if (event.target === this.selectAll) {
|
||||
this.checkboxes.forEach(checkbox => checkbox.checked = checked);
|
||||
this.values = checked ? this.initialValues.slice() : [];
|
||||
}
|
||||
else {
|
||||
let value = event.target.nextElementSibling.textContent;
|
||||
value = this.valueType === 'number' ? +value : value;
|
||||
|
||||
if (checked) {
|
||||
this.values.push(value);
|
||||
|
||||
if (this.values.length == this.initialValues.length) {
|
||||
this.selectAll.checked = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
let index = this.values.indexOf(value);
|
||||
this.values.splice(index, 1);
|
||||
this.selectAll.checked = false;
|
||||
}
|
||||
}
|
||||
this.filterActive = !this.selectAll.checked;
|
||||
this.filterChangedCallback();
|
||||
}
|
||||
|
||||
doesFilterPass(params: IDoesFilterPassParams): boolean {
|
||||
let { field } = this.filterParams.colDef;
|
||||
return this.values.includes(params.data[field]);
|
||||
}
|
||||
|
||||
getModel(): any {
|
||||
return this.isFilterActive() ? { value: this.values } : null;
|
||||
}
|
||||
|
||||
isFilterActive(): boolean {
|
||||
return this.filterActive;
|
||||
}
|
||||
|
||||
setModel(model: any): void | AgPromise<void> {
|
||||
this.values = model == null ? this.initialValues.slice() : model.value;
|
||||
let predicate = checkbox => model == null ? true : model.value.includes(checkbox.value);
|
||||
this.checkboxes.filter(predicate)
|
||||
.forEach(checkbox => checkbox.checked = true);
|
||||
this.filterActive = !this.selectAll.checked;
|
||||
this.filterChangedCallback();
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
this.checkboxes.forEach(checkBox => checkBox.removeEventListener('change', this.onCheckBoxChanged.bind(this)));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
import {
|
||||
DateTimeUtil,
|
||||
DefaultTooltip,
|
||||
GridCellTooltipUtils,
|
||||
GridColumnComparatorUtils,
|
||||
GridColumnKeyboardUtils,
|
||||
GridValueFormatterUtils,
|
||||
GridValueRendererUtils,
|
||||
PinnedType,
|
||||
} from "@webbpm/base-package";
|
||||
import {
|
||||
ColDef,
|
||||
DateFilter,
|
||||
ICellRendererFunc,
|
||||
SuppressKeyboardEventParams
|
||||
} from "ag-grid-community";
|
||||
import {
|
||||
StaticGridColumn
|
||||
} from "../../../generated/ru/micord/ervu/account_applications/component/property/grid/StaticGridColumn";
|
||||
import {CustomGridColumnFilterUtils} from "./CustomGridColumnFilterUtils";
|
||||
import {Moment} from "moment";
|
||||
import * as moment from "moment-timezone";
|
||||
|
||||
export class StaticColumnInitializer {
|
||||
|
||||
public static columnToColumnDef(gridRef: any, column: StaticGridColumn) {
|
||||
const columnDef: ColDef = {};
|
||||
columnDef.headerName = column.displayName ? column.displayName : '';
|
||||
columnDef.headerClass = "custom-header";
|
||||
columnDef.width = column.width;
|
||||
columnDef.suppressSizeToFit = column.widthFixed;
|
||||
columnDef.hide = column.hidden;
|
||||
columnDef.resizable = !column.widthFixed;
|
||||
columnDef.headerComponentParams = {"disable_hiding": column.disableHiding || false};
|
||||
columnDef.lockVisible = column.disableHiding;
|
||||
columnDef.headerTooltip = column.headerTooltip ? column.headerTooltip : column.displayName;
|
||||
columnDef.suppressMenu = column.suppressHeaderMenu;
|
||||
|
||||
if (column.pinned) {
|
||||
columnDef.pinned = column.pinned == PinnedType.LEFT ? 'left' : 'right';
|
||||
}
|
||||
columnDef['gridComp'] = this;
|
||||
columnDef.sortable = column.sortable;
|
||||
|
||||
if (column.sortable == null) {
|
||||
columnDef.sortable = true;
|
||||
}
|
||||
|
||||
if (column.autoHeight) {
|
||||
columnDef.autoHeight = column.autoHeight;
|
||||
columnDef.cellClass = 'ag-grid-cell-wrap-text';
|
||||
}
|
||||
|
||||
if (column.field) {
|
||||
columnDef.field = column.field.column;
|
||||
let type = column.field.type;
|
||||
|
||||
if (type != null) {
|
||||
if (gridRef.getRowModelType() == "clientSide") {
|
||||
columnDef.comparator = GridColumnComparatorUtils.columnComparator(type);
|
||||
}
|
||||
columnDef.valueFormatter = GridValueFormatterUtils.columnFormatter(type);
|
||||
columnDef.cellRenderer = gridRef.createRenderer(column);
|
||||
}
|
||||
|
||||
if (column.filter !== false) {
|
||||
columnDef.floatingFilter = gridRef.floatingFilter;
|
||||
columnDef.filter = CustomGridColumnFilterUtils.columnFilter(column.field.filterType);
|
||||
|
||||
if (columnDef.filter === DateFilter) {
|
||||
columnDef.filterParams = {
|
||||
comparator: function (filterLocalDateAtMidnight, cellValue) {
|
||||
|
||||
if (!cellValue) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
let filterMoment: Moment = moment.utc(filterLocalDateAtMidnight)
|
||||
.add(-filterLocalDateAtMidnight.getTimezoneOffset(), 'm');
|
||||
let cellMoment: Moment = DateTimeUtil.parseToMidnightUTC(cellValue);
|
||||
|
||||
if (filterMoment.isSame(cellMoment)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cellMoment.isBefore(filterMoment)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cellMoment.isAfter(filterMoment)) {
|
||||
return 1;
|
||||
}
|
||||
},
|
||||
browserDatePicker: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
columnDef.suppressKeyboardEvent = (params: SuppressKeyboardEventParams) => {
|
||||
return GridColumnKeyboardUtils.suppressHomeAndEndKeyboardEvent(params);
|
||||
}
|
||||
}
|
||||
|
||||
if (column.displayPopup) {
|
||||
const renderer: ICellRendererFunc = columnDef.cellRenderer as ICellRendererFunc;
|
||||
columnDef.cellRenderer = GridValueRendererUtils.tooltipValueRenderer(renderer);
|
||||
}
|
||||
else if (column.columnTooltip) {
|
||||
columnDef.tooltipComponent = DefaultTooltip;
|
||||
columnDef.tooltipValueGetter = GridCellTooltipUtils.fixedValueTooltip(column.columnTooltip);
|
||||
}
|
||||
else if (!gridRef.suppressColumnTooltip) {
|
||||
columnDef.tooltipComponent = DefaultTooltip;
|
||||
columnDef.tooltipValueGetter = GridCellTooltipUtils.defaultFormattedTooltip(
|
||||
column.field ? column.field.type : null);
|
||||
}
|
||||
|
||||
return columnDef;
|
||||
}
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ import {TokenInterceptor} from "./interceptor/token.interceptor.service";
|
|||
import {DropdownTreeViewComponent}from "../../account_applications/component/field/DropdownTreeViewComponent";
|
||||
import {DropdownTreeviewSelectComponent} from "../../account_applications/component/external/ngx-treeview/dropdown-treeview-select/dropdown-treeview-select.component";
|
||||
import {TreeviewModule} from "ngx-treeview";
|
||||
import {ErvuStaticGrid} from "../../account_applications/component/grid/ErvuStaticGrid";
|
||||
|
||||
registerLocaleData(localeRu);
|
||||
export const DIRECTIVES = [
|
||||
|
|
@ -34,7 +35,8 @@ export const DIRECTIVES = [
|
|||
forwardRef(() => VBoxLoadValues),
|
||||
forwardRef(() => DropdownTreeViewComponent),
|
||||
forwardRef(() => DropdownTreeviewSelectComponent),
|
||||
forwardRef(() => ErvuAccountTextFieldGridEditor)
|
||||
forwardRef(() => ErvuAccountTextFieldGridEditor),
|
||||
forwardRef(() => ErvuStaticGrid)
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
|
|
|||
62
resources/src/main/resources/StaticColumn.component
Normal file
62
resources/src/main/resources/StaticColumn.component
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<xmlComponent>
|
||||
<id>d60228dc-bcea-4942-b413-6816e2b568f1</id>
|
||||
<name>StaticColumn</name>
|
||||
<internal>false</internal>
|
||||
<versions>
|
||||
<studioVersion>3.186.1</studioVersion>
|
||||
<packageVersions>
|
||||
<entry>
|
||||
<key>ru.cg.webbpm.packages.base.resources</key>
|
||||
<value>3.192.3</value>
|
||||
</entry>
|
||||
</packageVersions>
|
||||
</versions>
|
||||
<rootObject id="d4f69cb0-864e-4895-b6fd-152072774909">
|
||||
<name>StaticColumn</name>
|
||||
<container>false</container>
|
||||
<childrenReordered>false</childrenReordered>
|
||||
<scripts id="bc6afb28-7884-477d-b425-2c2001be8379">
|
||||
<classRef type="TS">
|
||||
<className>GridV2Column</className>
|
||||
<packageName>component.grid</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
</scripts>
|
||||
<scripts id="a930059f-1e14-40ed-a4de-3dec3d03f9a7">
|
||||
<classRef type="JAVA">
|
||||
<className>StaticGridColumn</className>
|
||||
<packageName>ru.micord.ervu.account_applications.component.property.grid</packageName>
|
||||
</classRef>
|
||||
<enabled>true</enabled>
|
||||
<expanded>true</expanded>
|
||||
<properties>
|
||||
<entry>
|
||||
<key>autoHeight</key>
|
||||
<value>
|
||||
<simple>true</simple>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>displayPopup</key>
|
||||
<value>
|
||||
<simple>true</simple>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>filter</key>
|
||||
<value>
|
||||
<simple>null</simple>
|
||||
</value>
|
||||
</entry>
|
||||
<entry>
|
||||
<key>sortable</key>
|
||||
<value>
|
||||
<simple>true</simple>
|
||||
</value>
|
||||
</entry>
|
||||
</properties>
|
||||
</scripts>
|
||||
</rootObject>
|
||||
</xmlComponent>
|
||||
Loading…
Add table
Add a link
Reference in a new issue