mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-30 19:43:58 +00:00
form view - loading, navigation
This commit is contained in:
@@ -3,9 +3,18 @@ import _ from 'lodash';
|
|||||||
import { GridDisplay, ChangeCacheFunc, DisplayColumn, DisplayedColumnInfo, ChangeConfigFunc } from './GridDisplay';
|
import { GridDisplay, ChangeCacheFunc, DisplayColumn, DisplayedColumnInfo, ChangeConfigFunc } from './GridDisplay';
|
||||||
import { TableInfo, EngineDriver, ViewInfo, ColumnInfo, NamedObjectInfo, DatabaseInfo } from 'dbgate-types';
|
import { TableInfo, EngineDriver, ViewInfo, ColumnInfo, NamedObjectInfo, DatabaseInfo } from 'dbgate-types';
|
||||||
import { GridConfig, GridCache, createGridCache } from './GridConfig';
|
import { GridConfig, GridCache, createGridCache } from './GridConfig';
|
||||||
import { Expression, Select, treeToSql, dumpSqlSelect, mergeConditions, Condition } from 'dbgate-sqltree';
|
import {
|
||||||
|
Expression,
|
||||||
|
Select,
|
||||||
|
treeToSql,
|
||||||
|
dumpSqlSelect,
|
||||||
|
mergeConditions,
|
||||||
|
Condition,
|
||||||
|
OrderByExpression,
|
||||||
|
} from 'dbgate-sqltree';
|
||||||
import { filterName } from './filterName';
|
import { filterName } from './filterName';
|
||||||
import { TableGridDisplay } from './TableGridDisplay';
|
import { TableGridDisplay } from './TableGridDisplay';
|
||||||
|
import stableStringify from 'json-stable-stringify';
|
||||||
|
|
||||||
export class TableFormViewDisplay extends FormViewDisplay {
|
export class TableFormViewDisplay extends FormViewDisplay {
|
||||||
public table: TableInfo;
|
public table: TableInfo;
|
||||||
@@ -28,7 +37,7 @@ export class TableFormViewDisplay extends FormViewDisplay {
|
|||||||
this.columns = this.gridDisplay.columns;
|
this.columns = this.gridDisplay.columns;
|
||||||
}
|
}
|
||||||
|
|
||||||
getPrimaryKeyCondition(): Condition {
|
getPrimaryKeyEqualCondition(): Condition {
|
||||||
if (!this.config.formViewKey) return null;
|
if (!this.config.formViewKey) return null;
|
||||||
return {
|
return {
|
||||||
conditionType: 'and',
|
conditionType: 'and',
|
||||||
@@ -50,12 +59,130 @@ export class TableFormViewDisplay extends FormViewDisplay {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getCurrentRowQuery() {
|
getPrimaryKeyOperatorCondition(operator): Condition {
|
||||||
|
if (!this.config.formViewKey) return null;
|
||||||
|
const conditions = [];
|
||||||
|
|
||||||
|
const { primaryKey } = this.gridDisplay.baseTable;
|
||||||
|
for (let index = 0; index < primaryKey.columns.length; index++) {
|
||||||
|
conditions.push({
|
||||||
|
conditionType: 'and',
|
||||||
|
conditions: [
|
||||||
|
...primaryKey.columns.slice(0, index).map(({ columnName }) => ({
|
||||||
|
conditionType: 'binary',
|
||||||
|
operator: '=',
|
||||||
|
left: {
|
||||||
|
exprType: 'column',
|
||||||
|
columnName,
|
||||||
|
source: {
|
||||||
|
alias: 'basetbl',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
right: {
|
||||||
|
exprType: 'value',
|
||||||
|
value: this.config.formViewKey[columnName],
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
...primaryKey.columns.slice(index).map(({ columnName }) => ({
|
||||||
|
conditionType: 'binary',
|
||||||
|
operator: operator,
|
||||||
|
left: {
|
||||||
|
exprType: 'column',
|
||||||
|
columnName,
|
||||||
|
source: {
|
||||||
|
alias: 'basetbl',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
right: {
|
||||||
|
exprType: 'value',
|
||||||
|
value: this.config.formViewKey[columnName],
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conditions.length == 1) {
|
||||||
|
return conditions[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
conditionType: 'or',
|
||||||
|
conditions,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getSelect() {
|
||||||
if (!this.driver) return null;
|
if (!this.driver) return null;
|
||||||
const select = this.gridDisplay.createSelect();
|
const select = this.gridDisplay.createSelect();
|
||||||
if (!select) return null;
|
if (!select) return null;
|
||||||
select.topRecords = 1;
|
select.topRecords = 1;
|
||||||
select.where = mergeConditions(select.where, this.getPrimaryKeyCondition());
|
return select;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentRowQuery() {
|
||||||
|
const select = this.getSelect();
|
||||||
|
if (!select) return null;
|
||||||
|
|
||||||
|
select.where = mergeConditions(select.where, this.getPrimaryKeyEqualCondition());
|
||||||
|
const sql = treeToSql(this.driver, select, dumpSqlSelect);
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
extractKey(row) {
|
||||||
|
const formViewKey = _.pick(
|
||||||
|
row,
|
||||||
|
this.gridDisplay.baseTable.primaryKey.columns.map((x) => x.columnName)
|
||||||
|
);
|
||||||
|
return formViewKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
navigate(row) {
|
||||||
|
const formViewKey = this.extractKey(row);
|
||||||
|
this.setConfig((cfg) => ({
|
||||||
|
...cfg,
|
||||||
|
formViewKey,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoadedCurrentRow(row) {
|
||||||
|
if (!row) return false;
|
||||||
|
const formViewKey = this.extractKey(row);
|
||||||
|
return stableStringify(formViewKey) == stableStringify(this.config.formViewKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
navigateRowQuery(commmand: 'begin' | 'previous' | 'next' | 'end') {
|
||||||
|
if (!this.driver) return null;
|
||||||
|
const select = this.gridDisplay.createSelect();
|
||||||
|
if (!select) return null;
|
||||||
|
const { primaryKey } = this.gridDisplay.baseTable;
|
||||||
|
|
||||||
|
function getOrderBy(direction): OrderByExpression[] {
|
||||||
|
return primaryKey.columns.map(({ columnName }) => ({
|
||||||
|
exprType: 'column',
|
||||||
|
columnName,
|
||||||
|
direction,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
select.topRecords = 1;
|
||||||
|
switch (commmand) {
|
||||||
|
case 'begin':
|
||||||
|
select.orderBy = getOrderBy('ASC');
|
||||||
|
break;
|
||||||
|
case 'end':
|
||||||
|
select.orderBy = getOrderBy('DESC');
|
||||||
|
break;
|
||||||
|
case 'previous':
|
||||||
|
select.orderBy = getOrderBy('DESC');
|
||||||
|
select.where = mergeConditions(select.where, this.getPrimaryKeyOperatorCondition('<'));
|
||||||
|
break;
|
||||||
|
case 'next':
|
||||||
|
select.orderBy = getOrderBy('ASC');
|
||||||
|
select.where = mergeConditions(select.where, this.getPrimaryKeyOperatorCondition('>'));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
const sql = treeToSql(this.driver, select, dumpSqlSelect);
|
const sql = treeToSql(this.driver, select, dumpSqlSelect);
|
||||||
return sql;
|
return sql;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ const NullSpan = styled.span`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export default function FormView(props) {
|
export default function FormView(props) {
|
||||||
const { rowData, toolbarPortalRef, tabVisible, config, setConfig } = props;
|
const { rowData, toolbarPortalRef, tabVisible, config, setConfig, onNavigate } = props;
|
||||||
/** @type {import('dbgate-datalib').FormViewDisplay} */
|
/** @type {import('dbgate-datalib').FormViewDisplay} */
|
||||||
const formDisplay = props.formDisplay;
|
const formDisplay = props.formDisplay;
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
@@ -81,7 +81,10 @@ export default function FormView(props) {
|
|||||||
toolbarPortalRef &&
|
toolbarPortalRef &&
|
||||||
toolbarPortalRef.current &&
|
toolbarPortalRef.current &&
|
||||||
tabVisible &&
|
tabVisible &&
|
||||||
ReactDOM.createPortal(<FormViewToolbar switchToTable={handleSwitchToTable} />, toolbarPortalRef.current);
|
ReactDOM.createPortal(
|
||||||
|
<FormViewToolbar switchToTable={handleSwitchToTable} onNavigate={onNavigate} />,
|
||||||
|
toolbarPortalRef.current
|
||||||
|
);
|
||||||
|
|
||||||
// console.log('display', display);
|
// console.log('display', display);
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,24 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ToolbarButton from '../widgets/ToolbarButton';
|
import ToolbarButton from '../widgets/ToolbarButton';
|
||||||
|
|
||||||
export default function FormViewToolbar({ switchToTable }) {
|
export default function FormViewToolbar({ switchToTable, onNavigate }) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ToolbarButton onClick={switchToTable} icon="icon table">
|
<ToolbarButton onClick={switchToTable} icon="icon table">
|
||||||
Table view
|
Table view
|
||||||
</ToolbarButton>
|
</ToolbarButton>
|
||||||
|
<ToolbarButton onClick={() => onNavigate('begin')} icon="icon arrow-begin">
|
||||||
|
First
|
||||||
|
</ToolbarButton>
|
||||||
|
<ToolbarButton onClick={() => onNavigate('previous')} icon="icon arrow-left">
|
||||||
|
Previous
|
||||||
|
</ToolbarButton>
|
||||||
|
<ToolbarButton onClick={() => onNavigate('next')} icon="icon arrow-right">
|
||||||
|
Next
|
||||||
|
</ToolbarButton>
|
||||||
|
<ToolbarButton onClick={() => onNavigate('end')} icon="icon arrow-end">
|
||||||
|
Last
|
||||||
|
</ToolbarButton>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,10 @@ import useExtensions from '../utility/useExtensions';
|
|||||||
import FormView from './FormView';
|
import FormView from './FormView';
|
||||||
import axios from '../utility/axios';
|
import axios from '../utility/axios';
|
||||||
|
|
||||||
async function loadCurrentRow(props) {
|
async function loadRow(props, sql) {
|
||||||
const { formDisplay, conid, database } = props;
|
const { conid, database } = props;
|
||||||
/** @type {import('dbgate-datalib').TableFormViewDisplay} */
|
/** @type {import('dbgate-datalib').TableFormViewDisplay} */
|
||||||
|
const formDisplay = props.formDisplay;
|
||||||
const sql = formDisplay.getCurrentRowQuery();
|
|
||||||
|
|
||||||
const response = await axios.request({
|
const response = await axios.request({
|
||||||
url: 'database-connections/query-data',
|
url: 'database-connections/query-data',
|
||||||
@@ -31,12 +30,22 @@ export default function SqlFormView(props) {
|
|||||||
const [rowData, setRowData] = React.useState(null);
|
const [rowData, setRowData] = React.useState(null);
|
||||||
|
|
||||||
const handleLoadCurrentRow = async () => {
|
const handleLoadCurrentRow = async () => {
|
||||||
const row = await loadCurrentRow(props);
|
const row = await loadRow(props, formDisplay.getCurrentRowQuery());
|
||||||
if (row) setRowData(row);
|
if (row) setRowData(row);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleNavigate = async (command) => {
|
||||||
|
const row = await loadRow(props, formDisplay.navigateRowQuery(command));
|
||||||
|
if (row) {
|
||||||
|
setRowData(row);
|
||||||
|
formDisplay.navigate(row);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
handleLoadCurrentRow();
|
if (formDisplay && !formDisplay.isLoadedCurrentRow(rowData)) {
|
||||||
|
handleLoadCurrentRow();
|
||||||
|
}
|
||||||
}, [formDisplay]);
|
}, [formDisplay]);
|
||||||
|
|
||||||
// const { config, setConfig, cache, setCache, schemaName, pureName, conid, database } = props;
|
// const { config, setConfig, cache, setCache, schemaName, pureName, conid, database } = props;
|
||||||
@@ -67,5 +76,5 @@ export default function SqlFormView(props) {
|
|||||||
// setDisplay(newDisplay);
|
// setDisplay(newDisplay);
|
||||||
// }, [config, cache, conid, database, schemaName, pureName, dbinfo, extensions]);
|
// }, [config, cache, conid, database, schemaName, pureName, dbinfo, extensions]);
|
||||||
|
|
||||||
return <FormView {...props} rowData={rowData} />;
|
return <FormView {...props} rowData={rowData} onNavigate={handleNavigate} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ const iconNames = {
|
|||||||
'icon arrow-up': 'mdi mdi-arrow-up',
|
'icon arrow-up': 'mdi mdi-arrow-up',
|
||||||
'icon arrow-down': 'mdi mdi-arrow-down',
|
'icon arrow-down': 'mdi mdi-arrow-down',
|
||||||
'icon arrow-left': 'mdi mdi-arrow-left',
|
'icon arrow-left': 'mdi mdi-arrow-left',
|
||||||
|
'icon arrow-begin': 'mdi mdi-arrow-collapse-left',
|
||||||
|
'icon arrow-end': 'mdi mdi-arrow-collapse-right',
|
||||||
'icon arrow-right': 'mdi mdi-arrow-right',
|
'icon arrow-right': 'mdi mdi-arrow-right',
|
||||||
'icon format-code': 'mdi mdi-code-tags-check',
|
'icon format-code': 'mdi mdi-code-tags-check',
|
||||||
'icon show-wizard': 'mdi mdi-comment-edit',
|
'icon show-wizard': 'mdi mdi-comment-edit',
|
||||||
|
|||||||
Reference in New Issue
Block a user