mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-18 05:36:00 +00:00
314 lines
8.7 KiB
TypeScript
314 lines
8.7 KiB
TypeScript
import _ from 'lodash';
|
|
import { GridConfig, GridCache, GridConfigColumns } from './GridConfig';
|
|
import { ForeignKeyInfo, TableInfo, ColumnInfo, DbType, EngineDriver, NamedObjectInfo } from '@dbgate/types';
|
|
import { parseFilter, getFilterType } from '@dbgate/filterparser';
|
|
import { filterName } from './filterName';
|
|
import { ChangeSetFieldDefinition, ChangeSetRowDefinition } from './ChangeSet';
|
|
import { Expression, Select, treeToSql, dumpSqlSelect } from '@dbgate/sqltree';
|
|
|
|
export interface DisplayColumn {
|
|
schemaName: string;
|
|
pureName: string;
|
|
columnName: string;
|
|
headerText: string;
|
|
uniqueName: string;
|
|
uniquePath: string[];
|
|
notNull: boolean;
|
|
autoIncrement?: boolean;
|
|
isPrimaryKey?: boolean;
|
|
foreignKey?: ForeignKeyInfo;
|
|
isChecked?: boolean;
|
|
hintColumnName?: string;
|
|
commonType?: DbType;
|
|
}
|
|
|
|
export interface DisplayedColumnEx extends DisplayColumn {
|
|
sourceAlias: string;
|
|
}
|
|
|
|
export interface DisplayedColumnInfo {
|
|
[uniqueName: string]: DisplayedColumnEx;
|
|
}
|
|
|
|
export type ReferenceActionResult = 'noAction' | 'loadRequired' | 'refAdded';
|
|
|
|
export function combineReferenceActions(a: ReferenceActionResult, b: ReferenceActionResult): ReferenceActionResult {
|
|
if (a == 'loadRequired' || b == 'loadRequired') return 'loadRequired';
|
|
if (a == 'refAdded' || b == 'refAdded') return 'refAdded';
|
|
return 'noAction';
|
|
}
|
|
|
|
export type ChangeCacheFunc = (changeFunc: (config: GridCache) => GridCache) => void;
|
|
|
|
export abstract class GridDisplay {
|
|
constructor(
|
|
public config: GridConfig,
|
|
protected setConfig: (config: GridConfig) => void,
|
|
public cache: GridCache,
|
|
protected setCache: ChangeCacheFunc,
|
|
public driver?: EngineDriver
|
|
) {}
|
|
columns: DisplayColumn[];
|
|
baseTable?: TableInfo;
|
|
changeSetKeyFields: string[] = null;
|
|
sortable = false;
|
|
filterable = false;
|
|
editable = false;
|
|
|
|
setColumnVisibility(uniquePath: string[], isVisible: boolean) {
|
|
const uniqueName = uniquePath.join('.');
|
|
if (uniquePath.length == 1) {
|
|
this.includeInColumnSet('hiddenColumns', uniqueName, !isVisible);
|
|
} else {
|
|
this.includeInColumnSet('addedColumns', uniqueName, isVisible);
|
|
this.reload();
|
|
}
|
|
}
|
|
|
|
get engine() {
|
|
return this.driver?.engine;
|
|
}
|
|
|
|
reload() {
|
|
this.setCache((cache) => ({
|
|
...cache,
|
|
refreshTime: new Date().getTime(),
|
|
}));
|
|
}
|
|
|
|
includeInColumnSet(field: keyof GridConfigColumns, uniqueName: string, isIncluded: boolean) {
|
|
// console.log('includeInColumnSet', field, uniqueName, isIncluded);
|
|
if (isIncluded) {
|
|
this.setConfig({
|
|
...this.config,
|
|
[field]: [...(this.config[field] || []), uniqueName],
|
|
});
|
|
} else {
|
|
this.setConfig({
|
|
...this.config,
|
|
[field]: (this.config[field] || []).filter((x) => x != uniqueName),
|
|
});
|
|
}
|
|
}
|
|
|
|
showAllColumns() {
|
|
this.setConfig({
|
|
...this.config,
|
|
hiddenColumns: [],
|
|
});
|
|
}
|
|
|
|
hideAllColumns() {
|
|
this.setConfig({
|
|
...this.config,
|
|
hiddenColumns: this.columns.map((x) => x.uniqueName),
|
|
});
|
|
}
|
|
|
|
get hiddenColumnIndexes() {
|
|
return (this.config.hiddenColumns || []).map((x) => _.findIndex(this.columns, (y) => y.uniqueName == x));
|
|
}
|
|
|
|
isColumnChecked(column: DisplayColumn) {
|
|
// console.log('isColumnChecked', column, this.config.hiddenColumns);
|
|
return column.uniquePath.length == 1
|
|
? !this.config.hiddenColumns.includes(column.uniqueName)
|
|
: this.config.addedColumns.includes(column.uniqueName);
|
|
}
|
|
|
|
applyFilterOnSelect(select: Select, displayedColumnInfo: DisplayedColumnInfo) {
|
|
const conditions = [];
|
|
for (const uniqueName in this.config.filters) {
|
|
const filter = this.config.filters[uniqueName];
|
|
if (!filter) continue;
|
|
const column = displayedColumnInfo[uniqueName];
|
|
if (!column) continue;
|
|
try {
|
|
const condition = parseFilter(filter, getFilterType(column.commonType?.typeCode));
|
|
if (condition) {
|
|
conditions.push(
|
|
_.cloneDeepWith(condition, (expr: Expression) => {
|
|
if (expr.exprType == 'placeholder')
|
|
return {
|
|
exprType: 'column',
|
|
columnName: column.columnName,
|
|
source: { alias: column.sourceAlias },
|
|
};
|
|
})
|
|
);
|
|
}
|
|
} catch (err) {
|
|
console.warn(err.message);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (conditions.length > 0) {
|
|
select.where = {
|
|
conditionType: 'and',
|
|
conditions,
|
|
};
|
|
}
|
|
}
|
|
|
|
applySortOnSelect(select: Select, displayedColumnInfo: DisplayedColumnInfo) {
|
|
if (this.config.sort?.length > 0) {
|
|
select.orderBy = this.config.sort
|
|
.map((col) => ({ ...col, dispInfo: displayedColumnInfo[col.uniqueName] }))
|
|
.filter((col) => col.dispInfo)
|
|
.map((col) => ({
|
|
exprType: 'column',
|
|
columnName: col.dispInfo.columnName,
|
|
direction: col.order,
|
|
source: { alias: col.dispInfo.sourceAlias },
|
|
}));
|
|
}
|
|
}
|
|
|
|
getColumns(columnFilter) {
|
|
return this.columns.filter((col) => filterName(columnFilter, col.columnName));
|
|
}
|
|
|
|
getGridColumns() {
|
|
return this.getColumns(null).filter((x) => this.isColumnChecked(x));
|
|
}
|
|
|
|
isExpandedColumn(uniqueName: string) {
|
|
return this.config.expandedColumns.includes(uniqueName);
|
|
}
|
|
|
|
toggleExpandedColumn(uniqueName: string) {
|
|
this.includeInColumnSet('expandedColumns', uniqueName, !this.isExpandedColumn(uniqueName));
|
|
}
|
|
|
|
getFilter(uniqueName: string) {
|
|
return this.config.filters[uniqueName];
|
|
}
|
|
|
|
setFilter(uniqueName, value) {
|
|
this.setConfig({
|
|
...this.config,
|
|
filters: {
|
|
...this.config.filters,
|
|
[uniqueName]: value,
|
|
},
|
|
});
|
|
this.reload();
|
|
}
|
|
|
|
setSort(uniqueName, order) {
|
|
this.setConfig({
|
|
...this.config,
|
|
sort: [{ uniqueName, order }],
|
|
});
|
|
this.reload();
|
|
}
|
|
|
|
getSortOrder(uniqueName) {
|
|
return this.config.sort.find((x) => x.uniqueName == uniqueName)?.order;
|
|
}
|
|
|
|
get filterCount() {
|
|
return _.compact(_.values(this.config.filters)).length;
|
|
}
|
|
|
|
clearFilters() {
|
|
this.setConfig({
|
|
...this.config,
|
|
filters: {},
|
|
});
|
|
this.reload();
|
|
}
|
|
|
|
getChangeSetCondition(row) {
|
|
if (!this.changeSetKeyFields) return null;
|
|
return _.pick(row, this.changeSetKeyFields);
|
|
}
|
|
|
|
getChangeSetField(row, uniqueName, insertedRowIndex): ChangeSetFieldDefinition {
|
|
const col = this.columns.find((x) => x.uniqueName == uniqueName);
|
|
if (!col) return null;
|
|
if (!this.baseTable) return null;
|
|
if (this.baseTable.pureName != col.pureName || this.baseTable.schemaName != col.schemaName) return null;
|
|
return {
|
|
...this.getChangeSetRow(row, insertedRowIndex),
|
|
uniqueName: uniqueName,
|
|
columnName: col.columnName,
|
|
};
|
|
}
|
|
|
|
getChangeSetRow(row, insertedRowIndex): ChangeSetRowDefinition {
|
|
if (!this.baseTable) return null;
|
|
return {
|
|
pureName: this.baseTable.pureName,
|
|
schemaName: this.baseTable.schemaName,
|
|
insertedRowIndex,
|
|
condition: insertedRowIndex == null ? this.getChangeSetCondition(row) : null,
|
|
};
|
|
}
|
|
|
|
createSelect(): Select {
|
|
return null;
|
|
}
|
|
|
|
processReferences(select: Select, displayedColumnInfo: DisplayedColumnInfo): ReferenceActionResult {
|
|
return 'noAction';
|
|
}
|
|
|
|
createSelectBase(name: NamedObjectInfo, columns: ColumnInfo[]) {
|
|
if (!columns) return null;
|
|
const orderColumnName = columns[0].columnName;
|
|
const select: Select = {
|
|
commandType: 'select',
|
|
from: { name, alias: 'basetbl' },
|
|
columns: columns.map((col) => ({
|
|
exprType: 'column',
|
|
alias: col.columnName,
|
|
source: { alias: 'basetbl' },
|
|
...col,
|
|
})),
|
|
orderBy: [
|
|
{
|
|
exprType: 'column',
|
|
columnName: orderColumnName,
|
|
direction: 'ASC',
|
|
},
|
|
],
|
|
};
|
|
const displayedColumnInfo = _.keyBy(
|
|
this.columns.map((col) => ({ ...col, sourceAlias: 'basetbl' })),
|
|
'uniqueName'
|
|
);
|
|
const action = this.processReferences(select, displayedColumnInfo);
|
|
this.applyFilterOnSelect(select, displayedColumnInfo);
|
|
this.applySortOnSelect(select, displayedColumnInfo);
|
|
if (action == 'loadRequired') {
|
|
return null;
|
|
}
|
|
return select;
|
|
}
|
|
|
|
getPageQuery(offset: number, count: number) {
|
|
const select = this.createSelect();
|
|
if (!select) return null;
|
|
if (this.driver.dialect.rangeSelect) select.range = { offset: offset, limit: count };
|
|
else if (this.driver.dialect.limitSelect) select.topRecords = count;
|
|
const sql = treeToSql(this.driver, select, dumpSqlSelect);
|
|
return sql;
|
|
}
|
|
|
|
getCountQuery() {
|
|
const select = this.createSelect();
|
|
select.columns = [
|
|
{
|
|
exprType: 'raw',
|
|
sql: 'COUNT(*)',
|
|
alias: 'count',
|
|
},
|
|
];
|
|
select.orderBy = null;
|
|
const sql = treeToSql(this.driver, select, dumpSqlSelect);
|
|
return sql;
|
|
}
|
|
}
|