mirror of
https://github.com/dbgate/dbgate
synced 2024-11-07 20:26:23 +00:00
schema editing in dataset
This commit is contained in:
parent
675ef6e593
commit
cd1267b464
@ -9,7 +9,7 @@ import {
|
|||||||
AllowIdentityInsert,
|
AllowIdentityInsert,
|
||||||
Expression,
|
Expression,
|
||||||
} from 'dbgate-sqltree';
|
} from 'dbgate-sqltree';
|
||||||
import type { NamedObjectInfo, DatabaseInfo } from 'dbgate-types';
|
import type { NamedObjectInfo, DatabaseInfo, TableInfo } from 'dbgate-types';
|
||||||
|
|
||||||
export interface ChangeSetItem {
|
export interface ChangeSetItem {
|
||||||
pureName: string;
|
pureName: string;
|
||||||
@ -21,7 +21,16 @@ export interface ChangeSetItem {
|
|||||||
fields?: { [column: string]: string };
|
fields?: { [column: string]: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ChangeSetDataUpdateCommand {
|
||||||
|
type: 'renameField' | 'deleteField' | 'setField';
|
||||||
|
field: string;
|
||||||
|
value?: any;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ChangeSet {
|
export interface ChangeSet {
|
||||||
|
structure?: TableInfo;
|
||||||
|
dataUpdateCommands?: ChangeSetDataUpdateCommand[];
|
||||||
|
setColumnMode?: 'fixed' | 'variable';
|
||||||
inserts: ChangeSetItem[];
|
inserts: ChangeSetItem[];
|
||||||
updates: ChangeSetItem[];
|
updates: ChangeSetItem[];
|
||||||
deletes: ChangeSetItem[];
|
deletes: ChangeSetItem[];
|
||||||
@ -456,5 +465,12 @@ export function changeSetInsertDocuments(changeSet: ChangeSet, documents: any[],
|
|||||||
|
|
||||||
export function changeSetContainsChanges(changeSet: ChangeSet) {
|
export function changeSetContainsChanges(changeSet: ChangeSet) {
|
||||||
if (!changeSet) return false;
|
if (!changeSet) return false;
|
||||||
return changeSet.deletes.length > 0 || changeSet.updates.length > 0 || changeSet.inserts.length > 0;
|
return (
|
||||||
|
changeSet.deletes.length > 0 ||
|
||||||
|
changeSet.updates.length > 0 ||
|
||||||
|
changeSet.inserts.length > 0 ||
|
||||||
|
!!changeSet.structure ||
|
||||||
|
!!changeSet.setColumnMode ||
|
||||||
|
changeSet.dataUpdateCommands?.length > 0
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -84,6 +84,7 @@ export abstract class GridDisplay {
|
|||||||
return this.baseTable || this.baseView;
|
return this.baseTable || this.baseView;
|
||||||
}
|
}
|
||||||
changeSetKeyFields: string[] = null;
|
changeSetKeyFields: string[] = null;
|
||||||
|
editableStructure: TableInfo = null;
|
||||||
sortable = false;
|
sortable = false;
|
||||||
groupable = false;
|
groupable = false;
|
||||||
filterable = false;
|
filterable = false;
|
||||||
|
@ -24,6 +24,7 @@ export class JslGridDisplay extends GridDisplay {
|
|||||||
this.isDynamicStructure = isDynamicStructure;
|
this.isDynamicStructure = isDynamicStructure;
|
||||||
this.filterTypeOverride = 'eval';
|
this.filterTypeOverride = 'eval';
|
||||||
this.editable = editable;
|
this.editable = editable;
|
||||||
|
this.editableStructure = editable ? structure : null;
|
||||||
|
|
||||||
if (structure?.columns) {
|
if (structure?.columns) {
|
||||||
this.columns = _.uniqBy(
|
this.columns = _.uniqBy(
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import _, { indexOf, range } from 'lodash';
|
import _, { indexOf, range } from 'lodash';
|
||||||
import { GridDisplay } from 'dbgate-datalib';
|
import { ChangeSet, DisplayColumn, GridDisplay } from 'dbgate-datalib';
|
||||||
import { filterName } from 'dbgate-tools';
|
import { filterName } from 'dbgate-tools';
|
||||||
import CloseSearchButton from '../buttons/CloseSearchButton.svelte';
|
import CloseSearchButton from '../buttons/CloseSearchButton.svelte';
|
||||||
|
|
||||||
@ -14,6 +14,9 @@
|
|||||||
import keycodes from '../utility/keycodes';
|
import keycodes from '../utility/keycodes';
|
||||||
import ColumnManagerRow from './ColumnManagerRow.svelte';
|
import ColumnManagerRow from './ColumnManagerRow.svelte';
|
||||||
import { copyTextToClipboard } from '../utility/clipboard';
|
import { copyTextToClipboard } from '../utility/clipboard';
|
||||||
|
import SelectField from '../forms/SelectField.svelte';
|
||||||
|
import ColumnEditorModal from '../tableeditor/ColumnEditorModal.svelte';
|
||||||
|
import { tick } from 'svelte';
|
||||||
|
|
||||||
export let managerSize;
|
export let managerSize;
|
||||||
export let display: GridDisplay;
|
export let display: GridDisplay;
|
||||||
@ -21,6 +24,9 @@
|
|||||||
export let isDynamicStructure = false;
|
export let isDynamicStructure = false;
|
||||||
export let conid;
|
export let conid;
|
||||||
export let database;
|
export let database;
|
||||||
|
export let allowChangeChangeSetStructure = false;
|
||||||
|
export let changeSetState: { value: ChangeSet } = null;
|
||||||
|
export let dispatchChangeSet = null;
|
||||||
|
|
||||||
let filter;
|
let filter;
|
||||||
let domFocusField;
|
let domFocusField;
|
||||||
@ -103,8 +109,44 @@
|
|||||||
selectedColumns = value;
|
selectedColumns = value;
|
||||||
if (value.length > 0) currentColumnUniqueName = value[0];
|
if (value.length > 0) currentColumnUniqueName = value[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$: tableInfo = display?.editableStructure;
|
||||||
|
$: setTableInfo = updFunc => {
|
||||||
|
const structure = updFunc(display?.editableStructure);
|
||||||
|
dispatchChangeSet({
|
||||||
|
type: 'set',
|
||||||
|
value: {
|
||||||
|
...changeSetState?.value,
|
||||||
|
structure,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function handleAddColumn() {
|
||||||
|
showModal(ColumnEditorModal, {
|
||||||
|
setTableInfo,
|
||||||
|
tableInfo,
|
||||||
|
onAddNext: async () => {
|
||||||
|
await tick();
|
||||||
|
handleAddColumn();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
{#if allowChangeChangeSetStructure}
|
||||||
|
<div class="selectwrap">
|
||||||
|
<SelectField
|
||||||
|
isNative
|
||||||
|
class="colmode"
|
||||||
|
value="fixed"
|
||||||
|
options={[
|
||||||
|
{ label: 'Fixed columns (like SQL)', value: 'fixed' },
|
||||||
|
{ label: 'Variable columns (like MongoDB)', value: 'variable' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
<SearchBoxWrapper>
|
<SearchBoxWrapper>
|
||||||
<SearchInput placeholder="Search columns" bind:value={filter} />
|
<SearchInput placeholder="Search columns" bind:value={filter} />
|
||||||
<CloseSearchButton bind:filter />
|
<CloseSearchButton bind:filter />
|
||||||
@ -122,6 +164,9 @@
|
|||||||
}}>Add</InlineButton
|
}}>Add</InlineButton
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if allowChangeChangeSetStructure}
|
||||||
|
<InlineButton on:click={handleAddColumn}>Add</InlineButton>
|
||||||
|
{/if}
|
||||||
<InlineButton on:click={() => display.hideAllColumns()}>Hide</InlineButton>
|
<InlineButton on:click={() => display.hideAllColumns()}>Hide</InlineButton>
|
||||||
<InlineButton on:click={() => display.showAllColumns()}>Show</InlineButton>
|
<InlineButton on:click={() => display.showAllColumns()}>Show</InlineButton>
|
||||||
</SearchBoxWrapper>
|
</SearchBoxWrapper>
|
||||||
@ -139,12 +184,18 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{#each items as column (column.uniqueName)}
|
{#each items as column (column.uniqueName)}
|
||||||
|
{@const columnIndex = items.indexOf(column)}
|
||||||
<ColumnManagerRow
|
<ColumnManagerRow
|
||||||
{display}
|
{display}
|
||||||
{column}
|
{column}
|
||||||
{isJsonView}
|
{isJsonView}
|
||||||
{conid}
|
{conid}
|
||||||
{database}
|
{database}
|
||||||
|
{tableInfo}
|
||||||
|
{setTableInfo}
|
||||||
|
columnInfo={tableInfo?.columns?.[columnIndex]}
|
||||||
|
{columnIndex}
|
||||||
|
{allowChangeChangeSetStructure}
|
||||||
isSelected={selectedColumns.includes(column.uniqueName) || currentColumnUniqueName == column.uniqueName}
|
isSelected={selectedColumns.includes(column.uniqueName) || currentColumnUniqueName == column.uniqueName}
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
if (domFocusField) domFocusField.focus();
|
if (domFocusField) domFocusField.focus();
|
||||||
@ -198,4 +249,14 @@
|
|||||||
left: -1000px;
|
left: -1000px;
|
||||||
top: -1000px;
|
top: -1000px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.selectwrap :global(select) {
|
||||||
|
flex: 1;
|
||||||
|
padding: 3px 0px;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selectwrap {
|
||||||
|
border-bottom: 1px solid var(--theme-border);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
import FontIcon from '../icons/FontIcon.svelte';
|
import FontIcon from '../icons/FontIcon.svelte';
|
||||||
import ColumnLabel from '../elements/ColumnLabel.svelte';
|
import ColumnLabel from '../elements/ColumnLabel.svelte';
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
import { showModal } from '../modals/modalTools';
|
||||||
|
import ColumnEditorModal from '../tableeditor/ColumnEditorModal.svelte';
|
||||||
|
|
||||||
export let column;
|
export let column;
|
||||||
export let display;
|
export let display;
|
||||||
@ -12,6 +14,26 @@
|
|||||||
export let conid;
|
export let conid;
|
||||||
export let database;
|
export let database;
|
||||||
|
|
||||||
|
export let tableInfo;
|
||||||
|
export let setTableInfo;
|
||||||
|
|
||||||
|
export let columnInfo = null;
|
||||||
|
export let columnIndex = -1;
|
||||||
|
|
||||||
|
export let allowChangeChangeSetStructure = false;
|
||||||
|
|
||||||
|
function handleEditColumn() {
|
||||||
|
showModal(ColumnEditorModal, { columnInfo, tableInfo, setTableInfo });
|
||||||
|
}
|
||||||
|
|
||||||
|
function exchange(array, i1, i2) {
|
||||||
|
const i1r = (i1 + array.length) % array.length;
|
||||||
|
const i2r = (i2 + array.length) % array.length;
|
||||||
|
const res = [...array];
|
||||||
|
[res[i1r], res[i2r]] = [res[i2r], res[i1r]];
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -29,6 +51,7 @@
|
|||||||
on:mousemove
|
on:mousemove
|
||||||
on:mouseup
|
on:mouseup
|
||||||
>
|
>
|
||||||
|
<div>
|
||||||
<span class="expandColumnIcon" style={`margin-right: ${5 + (column.uniquePath.length - 1) * 10}px`}>
|
<span class="expandColumnIcon" style={`margin-right: ${5 + (column.uniquePath.length - 1) * 10}px`}>
|
||||||
<FontIcon
|
<FontIcon
|
||||||
icon={column.isExpandable ? plusExpandIcon(display.isExpandedColumn(column.uniqueName)) : 'icon invisible-box'}
|
icon={column.isExpandable ? plusExpandIcon(display.isExpandedColumn(column.uniqueName)) : 'icon invisible-box'}
|
||||||
@ -57,12 +80,44 @@
|
|||||||
<ColumnLabel {...column} showDataType {conid} {database} />
|
<ColumnLabel {...column} showDataType {conid} {database} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{#if allowChangeChangeSetStructure}
|
||||||
|
<div class="nowrap">
|
||||||
|
<span class="icon" on:click={handleEditColumn}>
|
||||||
|
<FontIcon icon="icon edit" />
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
on:click={() =>
|
||||||
|
setTableInfo(info => ({ ...info, columns: info.columns.filter(x => x.pairingId != columnInfo?.pairingId) }))}
|
||||||
|
>
|
||||||
|
<FontIcon icon="icon delete" />
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
on:click={() =>
|
||||||
|
setTableInfo(info => ({ ...info, columns: exchange(info.columns, columnIndex, columnIndex - 1) }))}
|
||||||
|
>
|
||||||
|
<FontIcon icon="icon arrow-up" />
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
on:click={() =>
|
||||||
|
setTableInfo(info => ({ ...info, columns: exchange(info.columns, columnIndex, columnIndex + 1) }))}
|
||||||
|
>
|
||||||
|
<FontIcon icon="icon arrow-down" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.row {
|
.row {
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
.row:hover {
|
.row:hover {
|
||||||
background: var(--theme-bg-hover);
|
background: var(--theme-bg-hover);
|
||||||
@ -71,4 +126,13 @@
|
|||||||
.row.isSelected {
|
.row.isSelected {
|
||||||
background: var(--theme-bg-selected);
|
background: var(--theme-bg-selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
position: relative;
|
||||||
|
/* top: 5px;
|
||||||
|
padding: 5px; */
|
||||||
|
}
|
||||||
|
.icon:hover {
|
||||||
|
background-color: var(--theme-bg-3);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createGridCache, createGridConfig, JslGridDisplay } from 'dbgate-datalib';
|
import { createGridCache, createGridConfig, JslGridDisplay } from 'dbgate-datalib';
|
||||||
|
import { generateTablePairingId } from 'dbgate-tools';
|
||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
import JslFormView from '../formview/JslFormView.svelte';
|
import JslFormView from '../formview/JslFormView.svelte';
|
||||||
import { apiOff, apiOn, useApiCall } from '../utility/api';
|
import { apiOff, apiOn, useApiCall } from '../utility/api';
|
||||||
@ -16,6 +17,8 @@
|
|||||||
export let changeSetStore = null;
|
export let changeSetStore = null;
|
||||||
export let dispatchChangeSet = null;
|
export let dispatchChangeSet = null;
|
||||||
|
|
||||||
|
export let allowChangeChangeSetStructure = false;
|
||||||
|
|
||||||
let loadedRows;
|
let loadedRows;
|
||||||
let infoCounter = 0;
|
let infoCounter = 0;
|
||||||
|
|
||||||
@ -44,7 +47,7 @@
|
|||||||
|
|
||||||
$: display = new JslGridDisplay(
|
$: display = new JslGridDisplay(
|
||||||
jslid,
|
jslid,
|
||||||
$info,
|
(allowChangeChangeSetStructure && changeSetState?.value?.structure) || generateTablePairingId($info),
|
||||||
$config,
|
$config,
|
||||||
config.update,
|
config.update,
|
||||||
$cache,
|
$cache,
|
||||||
@ -71,5 +74,6 @@
|
|||||||
{changeSetState}
|
{changeSetState}
|
||||||
{changeSetStore}
|
{changeSetStore}
|
||||||
{dispatchChangeSet}
|
{dispatchChangeSet}
|
||||||
|
{allowChangeChangeSetStructure}
|
||||||
/>
|
/>
|
||||||
{/key}
|
{/key}
|
||||||
|
@ -13,10 +13,10 @@
|
|||||||
import { editorAddColumn, editorDeleteColumn, editorModifyColumn, fillEditorColumnInfo } from 'dbgate-tools';
|
import { editorAddColumn, editorDeleteColumn, editorModifyColumn, fillEditorColumnInfo } from 'dbgate-tools';
|
||||||
|
|
||||||
export let columnInfo;
|
export let columnInfo;
|
||||||
export let setTableInfo;
|
export let setTableInfo = null;
|
||||||
export let tableInfo;
|
export let tableInfo = null;
|
||||||
export let onAddNext;
|
export let onAddNext;
|
||||||
export let driver;
|
export let driver = null;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<FormProvider initialValues={fillEditorColumnInfo(columnInfo || {}, tableInfo)}>
|
<FormProvider initialValues={fillEditorColumnInfo(columnInfo || {}, tableInfo)}>
|
||||||
@ -31,7 +31,10 @@
|
|||||||
<FormCheckboxField name="notNull" label="NOT NULL" />
|
<FormCheckboxField name="notNull" label="NOT NULL" />
|
||||||
<FormCheckboxField name="isPrimaryKey" label="Is Primary Key" />
|
<FormCheckboxField name="isPrimaryKey" label="Is Primary Key" />
|
||||||
<FormCheckboxField name="autoIncrement" label="Is Autoincrement" />
|
<FormCheckboxField name="autoIncrement" label="Is Autoincrement" />
|
||||||
<FormTextField name="defaultValue" label="Default value. Please use valid SQL expression, eg. 'Hello World' for string value, '' for empty string" />
|
<FormTextField
|
||||||
|
name="defaultValue"
|
||||||
|
label="Default value. Please use valid SQL expression, eg. 'Hello World' for string value, '' for empty string"
|
||||||
|
/>
|
||||||
<FormTextField name="computedExpression" label="Computed expression" />
|
<FormTextField name="computedExpression" label="Computed expression" />
|
||||||
{#if driver?.dialect?.columnProperties?.isUnsigned}
|
{#if driver?.dialect?.columnProperties?.isUnsigned}
|
||||||
<FormCheckboxField name="isUnsigned" label="Unsigned" />
|
<FormCheckboxField name="isUnsigned" label="Unsigned" />
|
||||||
|
@ -87,6 +87,7 @@
|
|||||||
<JslDataGrid
|
<JslDataGrid
|
||||||
jslid={jslid || `archive://${archiveFolder}/${archiveFile}`}
|
jslid={jslid || `archive://${archiveFolder}/${archiveFile}`}
|
||||||
supportsReload
|
supportsReload
|
||||||
|
allowChangeChangeSetStructure
|
||||||
changeSetState={$changeSetStore}
|
changeSetState={$changeSetStore}
|
||||||
focusOnVisible
|
focusOnVisible
|
||||||
{changeSetStore}
|
{changeSetStore}
|
||||||
|
Loading…
Reference in New Issue
Block a user