mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-28 01:26:01 +00:00
jsl grid display
This commit is contained in:
@@ -58,13 +58,13 @@ module.exports = {
|
||||
},
|
||||
|
||||
getInfo_meta: 'get',
|
||||
getInfo(jslid) {
|
||||
getInfo({ jslid }) {
|
||||
const file = path.join(jsldir(), `${jslid}.jsonl.info`);
|
||||
return JSON.parse(fs.readFileSync(file, 'utf-8'));
|
||||
},
|
||||
|
||||
getRows_meta: 'get',
|
||||
async getRows(jslid, offset, limit) {
|
||||
async getRows({ jslid, offset, limit }) {
|
||||
await this.ensureReader(jslid, offset);
|
||||
const res = [];
|
||||
for (let i = 0; i < limit; i += 1) {
|
||||
|
||||
@@ -6,12 +6,14 @@ const io = require('socket.io');
|
||||
const fs = require('fs');
|
||||
|
||||
const useController = require('./utility/useController');
|
||||
const socket = require('./utility/socket');
|
||||
|
||||
const connections = require('./controllers/connections');
|
||||
const serverConnections = require('./controllers/serverConnections');
|
||||
const databaseConnections = require('./controllers/databaseConnections');
|
||||
const tables = require('./controllers/tables');
|
||||
const sessions = require('./controllers/sessions');
|
||||
const socket = require('./utility/socket');
|
||||
const jsldata = require('./controllers/jsldata');
|
||||
|
||||
function start() {
|
||||
console.log('process.argv', process.argv);
|
||||
@@ -29,6 +31,7 @@ function start() {
|
||||
useController(app, '/database-connections', databaseConnections);
|
||||
useController(app, '/tables', tables);
|
||||
useController(app, '/sessions', sessions);
|
||||
useController(app, '/jsldata', jsldata);
|
||||
|
||||
if (fs.existsSync('/home/dbgate-docker/build')) {
|
||||
// server static files inside docker container
|
||||
|
||||
@@ -7,16 +7,16 @@ import { Select, Expression } from '@dbgate/sqltree';
|
||||
import { ChangeSetFieldDefinition, ChangeSetRowDefinition } from './ChangeSet';
|
||||
|
||||
export interface DisplayColumn {
|
||||
schemaName: string;
|
||||
pureName: string;
|
||||
schemaName?: string;
|
||||
pureName?: string;
|
||||
columnName: string;
|
||||
headerText: string;
|
||||
uniqueName: string;
|
||||
uniquePath: string[];
|
||||
notNull: boolean;
|
||||
autoIncrement: boolean;
|
||||
isPrimaryKey: boolean;
|
||||
foreignKey: ForeignKeyInfo;
|
||||
autoIncrement?: boolean;
|
||||
isPrimaryKey?: boolean;
|
||||
foreignKey?: ForeignKeyInfo;
|
||||
isChecked?: boolean;
|
||||
hintColumnName?: string;
|
||||
commonType?: DbType;
|
||||
@@ -47,9 +47,11 @@ export abstract class GridDisplay {
|
||||
public cache: GridCache,
|
||||
protected setCache: ChangeCacheFunc,
|
||||
protected getTableInfo: ({ schemaName, pureName }) => Promise<TableInfo>,
|
||||
public driver: EngineDriver
|
||||
public driver?: EngineDriver
|
||||
) {}
|
||||
abstract getPageQuery(offset: number, count: number): string;
|
||||
getPageQuery(offset: number, count: number): string {
|
||||
return null;
|
||||
}
|
||||
columns: DisplayColumn[];
|
||||
baseTable?: TableInfo;
|
||||
changeSetKeyFields: string[] = null;
|
||||
@@ -65,7 +67,7 @@ export abstract class GridDisplay {
|
||||
}
|
||||
|
||||
get engine() {
|
||||
return this.driver.engine;
|
||||
return this.driver?.engine;
|
||||
}
|
||||
|
||||
reload() {
|
||||
|
||||
24
packages/datalib/src/JslGridDisplay.ts
Normal file
24
packages/datalib/src/JslGridDisplay.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { GridDisplay, ChangeCacheFunc } from './GridDisplay';
|
||||
import { QueryResultColumn } from '@dbgate/types';
|
||||
import { GridConfig, GridCache } from './GridConfig';
|
||||
|
||||
export class JslGridDisplay extends GridDisplay {
|
||||
constructor(
|
||||
jslid,
|
||||
columns: QueryResultColumn[],
|
||||
config: GridConfig,
|
||||
setConfig: (config: GridConfig) => void,
|
||||
cache: GridCache,
|
||||
setCache: ChangeCacheFunc
|
||||
) {
|
||||
super(config, setConfig, cache, setCache, null, null);
|
||||
this.columns = columns.map((col) => ({
|
||||
columnName: col.columnName,
|
||||
headerText: col.columnName,
|
||||
uniqueName: col.columnName,
|
||||
uniquePath: [col.columnName],
|
||||
notNull: col.notNull,
|
||||
autoIncrement: col.autoIncrement,
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
export * from "./GridDisplay";
|
||||
export * from "./GridConfig";
|
||||
export * from "./TableGridDisplay";
|
||||
export * from "./JslGridDisplay";
|
||||
export * from "./ChangeSet";
|
||||
export * from "./filterName";
|
||||
|
||||
@@ -14,7 +14,12 @@ const dialect = {
|
||||
};
|
||||
|
||||
function extractColumns(columns) {
|
||||
return _.sortBy(_.values(columns), 'index')
|
||||
return _.sortBy(_.values(columns), 'index').map((col) => ({
|
||||
...col,
|
||||
columnName: col.name,
|
||||
notNull: !col.nullable,
|
||||
autoIncrement: !!col.identity,
|
||||
}));
|
||||
}
|
||||
|
||||
/** @type {import('@dbgate/types').EngineDriver} */
|
||||
|
||||
4
packages/types/query.d.ts
vendored
4
packages/types/query.d.ts
vendored
@@ -4,7 +4,9 @@ export interface RangeDefinition {
|
||||
}
|
||||
|
||||
export interface QueryResultColumn {
|
||||
name: string;
|
||||
columnName: string;
|
||||
notNull: boolean;
|
||||
autoIncrement?: boolean;
|
||||
}
|
||||
|
||||
export interface QueryResult {
|
||||
|
||||
@@ -108,6 +108,47 @@ const FocusField = styled.input`
|
||||
top: -1000px;
|
||||
`;
|
||||
|
||||
/** @param props {import('./types').DataGridProps} */
|
||||
async function loadDataPage(props, offset, limit) {
|
||||
const { display, conid, database, jslid } = props;
|
||||
|
||||
console.log('LOAD PAGE', jslid);
|
||||
|
||||
if (jslid) {
|
||||
const response = await axios.request({
|
||||
url: 'jsldata/get-rows',
|
||||
method: 'post',
|
||||
params: {
|
||||
jslid,
|
||||
offset,
|
||||
limit,
|
||||
},
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
const sql = display.getPageQuery(offset, limit);
|
||||
|
||||
const response = await axios.request({
|
||||
url: 'database-connections/query-data',
|
||||
method: 'post',
|
||||
params: {
|
||||
conid,
|
||||
database,
|
||||
},
|
||||
data: { sql },
|
||||
});
|
||||
|
||||
return response.data.rows;
|
||||
}
|
||||
|
||||
function dataPageAvailable(props) {
|
||||
const { display, conid, database, jslid } = props;
|
||||
if (jslid) return true;
|
||||
const sql = display.getPageQuery(0, 1);
|
||||
return !!sql;
|
||||
}
|
||||
|
||||
/** @param props {import('./types').DataGridProps} */
|
||||
export default function DataGridCore(props) {
|
||||
const { conid, database, display, changeSetState, dispatchChangeSet, tabVisible } = props;
|
||||
@@ -146,8 +187,8 @@ export default function DataGridCore(props) {
|
||||
// const [inplaceEditorShouldSave, setInplaceEditorShouldSave] = React.useState(false);
|
||||
// const [inplaceEditorChangedOnCreate, setInplaceEditorChangedOnCreate] = React.useState(false);
|
||||
|
||||
const changeSet = changeSetState.value;
|
||||
const setChangeSet = React.useCallback(value => dispatchChangeSet({ type: 'set', value }), [dispatchChangeSet]);
|
||||
const changeSet = changeSetState && changeSetState.value;
|
||||
const setChangeSet = React.useCallback((value) => dispatchChangeSet({ type: 'set', value }), [dispatchChangeSet]);
|
||||
|
||||
const changeSetRef = React.useRef(changeSet);
|
||||
|
||||
@@ -155,8 +196,8 @@ export default function DataGridCore(props) {
|
||||
|
||||
const autofillMarkerCell = React.useMemo(
|
||||
() =>
|
||||
selectedCells && selectedCells.length > 0 && _.uniq(selectedCells.map(x => x[0])).length == 1
|
||||
? [_.max(selectedCells.map(x => x[0])), _.max(selectedCells.map(x => x[1]))]
|
||||
selectedCells && selectedCells.length > 0 && _.uniq(selectedCells.map((x) => x[0])).length == 1
|
||||
? [_.max(selectedCells.map((x) => x[0])), _.max(selectedCells.map((x) => x[1]))]
|
||||
: null,
|
||||
[selectedCells]
|
||||
);
|
||||
@@ -170,17 +211,7 @@ export default function DataGridCore(props) {
|
||||
const loadStart = new Date().getTime();
|
||||
loadedTimeRef.current = loadStart;
|
||||
|
||||
const sql = display.getPageQuery(loadedRows.length, 100);
|
||||
|
||||
const response = await axios.request({
|
||||
url: 'database-connections/query-data',
|
||||
method: 'post',
|
||||
params: {
|
||||
conid,
|
||||
database,
|
||||
},
|
||||
data: { sql },
|
||||
});
|
||||
const nextRows = await loadDataPage(props, loadedRows.length, 100);
|
||||
if (loadedTimeRef.current !== loadStart) {
|
||||
// new load was dispatched
|
||||
return;
|
||||
@@ -189,7 +220,6 @@ export default function DataGridCore(props) {
|
||||
// console.log('Error loading data from server', nextRows);
|
||||
// nextRows = [];
|
||||
// }
|
||||
const { rows: nextRows } = response.data;
|
||||
// console.log('nextRows', nextRows);
|
||||
const loadedInfo = {
|
||||
loadedRows: [...loadedRows, ...nextRows],
|
||||
@@ -288,9 +318,10 @@ export default function DataGridCore(props) {
|
||||
firstVisibleRowScrollIndex + visibleRowCountUpperBound >= loadedRows.length &&
|
||||
insertedRows.length == 0
|
||||
) {
|
||||
const sql = display.getPageQuery(0, 1);
|
||||
// try to get SQL, if success, load page. If not, callbacks to load missing metadata are dispatched
|
||||
if (sql) loadNextData();
|
||||
if (dataPageAvailable(props)) {
|
||||
// If not, callbacks to load missing metadata are dispatched
|
||||
loadNextData();
|
||||
}
|
||||
}
|
||||
if (display.cache.refreshTime > loadedTime) {
|
||||
reload();
|
||||
@@ -327,7 +358,7 @@ export default function DataGridCore(props) {
|
||||
|
||||
const realColumnUniqueNames = React.useMemo(
|
||||
() =>
|
||||
_.range(columnSizes.realCount).map(realIndex => (columns[columnSizes.realToModel(realIndex)] || {}).uniqueName),
|
||||
_.range(columnSizes.realCount).map((realIndex) => (columns[columnSizes.realToModel(realIndex)] || {}).uniqueName),
|
||||
[columnSizes, columns]
|
||||
);
|
||||
|
||||
@@ -335,15 +366,15 @@ export default function DataGridCore(props) {
|
||||
const insertedRows = getChangeSetInsertedRows(changeSet, display.baseTable);
|
||||
const rowCountNewIncluded = loadedRows.length + insertedRows.length;
|
||||
|
||||
const handleRowScroll = value => {
|
||||
const handleRowScroll = (value) => {
|
||||
setFirstVisibleRowScrollIndex(value);
|
||||
};
|
||||
|
||||
const handleColumnScroll = value => {
|
||||
const handleColumnScroll = (value) => {
|
||||
setFirstVisibleColumnScrollIndex(value);
|
||||
};
|
||||
|
||||
const handleContextMenu = event => {
|
||||
const handleContextMenu = (event) => {
|
||||
event.preventDefault();
|
||||
showMenu(
|
||||
event.pageX,
|
||||
@@ -407,7 +438,7 @@ export default function DataGridCore(props) {
|
||||
const pasteRows = pastedText
|
||||
.replace(/\r/g, '')
|
||||
.split('\n')
|
||||
.map(row => row.split('\t'));
|
||||
.map((row) => row.split('\t'));
|
||||
let chs = changeSet;
|
||||
let allRows = loadedAndInsertedRows;
|
||||
|
||||
@@ -439,8 +470,8 @@ export default function DataGridCore(props) {
|
||||
}
|
||||
if (selectedCells.length > 1) {
|
||||
const regularSelected = selectedCells.filter(isRegularCell);
|
||||
const startRow = _.min(regularSelected.map(x => x[0]));
|
||||
const startCol = _.min(regularSelected.map(x => x[1]));
|
||||
const startRow = _.min(regularSelected.map((x) => x[0]));
|
||||
const startCol = _.min(regularSelected.map((x) => x[1]));
|
||||
for (const cell of regularSelected) {
|
||||
const [rowIndex, colIndex] = cell;
|
||||
const selectionRow = rowIndex - startRow;
|
||||
@@ -464,16 +495,16 @@ export default function DataGridCore(props) {
|
||||
}
|
||||
|
||||
function copyToClipboard() {
|
||||
const rowIndexes = _.uniq(selectedCells.map(x => x[0])).sort();
|
||||
const lines = rowIndexes.map(rowIndex => {
|
||||
const rowIndexes = _.uniq(selectedCells.map((x) => x[0])).sort();
|
||||
const lines = rowIndexes.map((rowIndex) => {
|
||||
const colIndexes = selectedCells
|
||||
.filter(x => x[0] == rowIndex)
|
||||
.map(x => x[1])
|
||||
.filter((x) => x[0] == rowIndex)
|
||||
.map((x) => x[1])
|
||||
.sort();
|
||||
const rowData = loadedAndInsertedRows[rowIndex];
|
||||
const line = colIndexes
|
||||
.map(col => realColumnUniqueNames[col])
|
||||
.map(col => (rowData[col] == null ? '' : rowData[col]))
|
||||
.map((col) => realColumnUniqueNames[col])
|
||||
.map((col) => (rowData[col] == null ? '' : rowData[col]))
|
||||
.join('\t');
|
||||
return line;
|
||||
});
|
||||
@@ -485,7 +516,7 @@ export default function DataGridCore(props) {
|
||||
if (autofillDragStartCell) {
|
||||
const cell = cellFromEvent(event);
|
||||
if (isRegularCell(cell) && (cell[0] == autofillDragStartCell[0] || cell[1] == autofillDragStartCell[1])) {
|
||||
const autoFillStart = [selectedCells[0][0], _.min(selectedCells.map(x => x[1]))];
|
||||
const autoFillStart = [selectedCells[0][0], _.min(selectedCells.map((x) => x[1]))];
|
||||
// @ts-ignore
|
||||
setAutofillSelectedCells(getCellRange(autoFillStart, cell));
|
||||
}
|
||||
@@ -506,9 +537,9 @@ export default function DataGridCore(props) {
|
||||
if (autofillDragStartCell) {
|
||||
const currentRowNumber = currentCell[0];
|
||||
if (_.isNumber(currentRowNumber)) {
|
||||
const rowIndexes = _.uniq((autofillSelectedCells || []).map(x => x[0])).filter(x => x != currentRowNumber);
|
||||
const rowIndexes = _.uniq((autofillSelectedCells || []).map((x) => x[0])).filter((x) => x != currentRowNumber);
|
||||
// @ts-ignore
|
||||
const colNames = selectedCells.map(cell => realColumnUniqueNames[cell[1]]);
|
||||
const colNames = selectedCells.map((cell) => realColumnUniqueNames[cell[1]]);
|
||||
const changeObject = _.pick(loadedAndInsertedRows[currentRowNumber], colNames);
|
||||
setChangeSet(
|
||||
batchUpdateChangeSet(
|
||||
@@ -539,7 +570,7 @@ export default function DataGridCore(props) {
|
||||
}
|
||||
|
||||
function getSelectedRowDefinitions() {
|
||||
return getRowDefinitions(_.uniq((selectedCells || []).map(x => x[0])));
|
||||
return getRowDefinitions(_.uniq((selectedCells || []).map((x) => x[0])));
|
||||
}
|
||||
|
||||
function revertRowChanges() {
|
||||
@@ -874,7 +905,7 @@ export default function DataGridCore(props) {
|
||||
<TableHead>
|
||||
<TableHeaderRow ref={headerRowRef}>
|
||||
<TableHeaderCell data-row="header" data-col="header" />
|
||||
{visibleRealColumns.map(col => (
|
||||
{visibleRealColumns.map((col) => (
|
||||
<TableHeaderCell
|
||||
data-row="header"
|
||||
data-col={col.colIndex}
|
||||
@@ -883,7 +914,7 @@ export default function DataGridCore(props) {
|
||||
>
|
||||
<ColumnHeaderControl
|
||||
column={col}
|
||||
setSort={order => display.setSort(col.uniqueName, order)}
|
||||
setSort={(order) => display.setSort(col.uniqueName, order)}
|
||||
order={display.getSortOrder(col.uniqueName)}
|
||||
/>
|
||||
</TableHeaderCell>
|
||||
@@ -901,7 +932,7 @@ export default function DataGridCore(props) {
|
||||
</InlineButton>
|
||||
)}
|
||||
</TableHeaderCell>
|
||||
{visibleRealColumns.map(col => (
|
||||
{visibleRealColumns.map((col) => (
|
||||
<TableFilterCell
|
||||
key={col.uniqueName}
|
||||
style={{ width: col.widthPx, minWidth: col.widthPx, maxWidth: col.widthPx }}
|
||||
@@ -911,7 +942,7 @@ export default function DataGridCore(props) {
|
||||
<DataFilterControl
|
||||
filterType={getFilterType(col.commonType ? col.commonType.typeCode : null)}
|
||||
filter={display.getFilter(col.uniqueName)}
|
||||
setFilter={value => display.setFilter(col.uniqueName, value)}
|
||||
setFilter={(value) => display.setFilter(col.uniqueName, value)}
|
||||
/>
|
||||
</TableFilterCell>
|
||||
))}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { GridDisplay, ChangeSet } from '@dbgate/datalib';
|
||||
|
||||
export interface DataGridProps {
|
||||
conid?: number;
|
||||
conid?: string;
|
||||
database?: string;
|
||||
display: GridDisplay;
|
||||
tabVisible?: boolean;
|
||||
changeSetState?: { value: ChangeSet };
|
||||
dispatchChangeSet?: Function;
|
||||
toolbarPortalRef?: any;
|
||||
jslid?: string;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
import React from 'react';
|
||||
import DataGrid from '../datagrid/DataGrid';
|
||||
import { JslGridDisplay, createGridConfig, createGridCache } from '@dbgate/datalib';
|
||||
import useFetch from '../utility/useFetch';
|
||||
|
||||
export default function JslDataGrid({ jslid }) {
|
||||
return <div>{jslid}</div>;
|
||||
// const display=React.useMemo(()=>)
|
||||
// return <DataGrid />;
|
||||
const columns = useFetch({
|
||||
params: { jslid },
|
||||
url: 'jsldata/get-info',
|
||||
defaultValue: [],
|
||||
});
|
||||
const [config, setConfig] = React.useState(createGridConfig());
|
||||
const [cache, setCache] = React.useState(createGridCache());
|
||||
const display = React.useMemo(() => new JslGridDisplay(jslid, columns, config, setConfig, cache, setCache), [
|
||||
jslid,
|
||||
columns,
|
||||
]);
|
||||
|
||||
return <DataGrid display={display} jslid={jslid} />;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,9 @@ const EditorContainer = styled.div`
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const MessagesContainer = styled.div``;
|
||||
const MessagesContainer = styled.div`
|
||||
height: 200px;
|
||||
`;
|
||||
|
||||
export default function QueryTab({ tabid, conid, database, tabVisible, toolbarPortalRef }) {
|
||||
const localStorageKey = `sql_${tabid}`;
|
||||
|
||||
@@ -22,7 +22,10 @@ const TabNameWrapper = styled.span`
|
||||
margin-left: 5px;
|
||||
`;
|
||||
|
||||
const TabContainer = styled.div``;
|
||||
const TabContainer = styled.div`
|
||||
position: relative;
|
||||
flex-grow: 1;
|
||||
`;
|
||||
|
||||
const TabsContainer = styled.div`
|
||||
display: flex;
|
||||
@@ -31,6 +34,11 @@ const TabsContainer = styled.div`
|
||||
background-color: ${theme.tabsPanel.background};
|
||||
`;
|
||||
|
||||
const MainContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
export function TabPage({ label = undefined, children }) {
|
||||
return children;
|
||||
}
|
||||
@@ -39,7 +47,7 @@ export function TabControl({ children }) {
|
||||
const [value, setValue] = React.useState(0);
|
||||
const childrenArray = (_.isArray(children) ? _.flatten(children) : [children]).filter((x) => x);
|
||||
return (
|
||||
<div>
|
||||
<MainContainer>
|
||||
<TabsContainer>
|
||||
{childrenArray
|
||||
.filter((x) => x.props)
|
||||
@@ -51,6 +59,6 @@ export function TabControl({ children }) {
|
||||
))}
|
||||
</TabsContainer>
|
||||
{<TabContainer key={value}>{childrenArray[value] && childrenArray[value].props.children}</TabContainer>}
|
||||
</div>
|
||||
</MainContainer>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user