mirror of
https://github.com/Fabio286/antares.git
synced 2025-06-05 21:59:22 +02:00
refactor: ts and composition api on missing components
This commit is contained in:
@ -92,13 +92,15 @@ export interface TableInfos {
|
|||||||
collation: string;
|
collation: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type TableOptions = Partial<TableInfos>;
|
||||||
|
|
||||||
export interface TableField {
|
export interface TableField {
|
||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
_antares_id?: string;
|
_antares_id?: string;
|
||||||
name: string;
|
name: string;
|
||||||
key: string;
|
|
||||||
type: string;
|
type: string;
|
||||||
schema: string;
|
schema: string;
|
||||||
|
table?: string;
|
||||||
numPrecision?: number;
|
numPrecision?: number;
|
||||||
numLength?: number;
|
numLength?: number;
|
||||||
datePrecision?: number;
|
datePrecision?: number;
|
||||||
@ -109,6 +111,7 @@ export interface TableField {
|
|||||||
zerofill?: boolean;
|
zerofill?: boolean;
|
||||||
order?: number;
|
order?: number;
|
||||||
default?: string;
|
default?: string;
|
||||||
|
defaultType?: string;
|
||||||
enumValues?: string;
|
enumValues?: string;
|
||||||
charset?: string;
|
charset?: string;
|
||||||
collation?: string;
|
collation?: string;
|
||||||
@ -118,7 +121,11 @@ export interface TableField {
|
|||||||
comment?: string;
|
comment?: string;
|
||||||
after?: string;
|
after?: string;
|
||||||
orgName?: string;
|
orgName?: string;
|
||||||
length?: number;
|
length?: number | false;
|
||||||
|
alias: string;
|
||||||
|
tableAlias: string;
|
||||||
|
orgTable: string;
|
||||||
|
key?: 'pri' | 'uni';
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TableIndex {
|
export interface TableIndex {
|
||||||
@ -136,6 +143,8 @@ export interface TableIndex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface TableForeign {
|
export interface TableForeign {
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
_antares_id?: string;
|
||||||
constraintName: string;
|
constraintName: string;
|
||||||
refSchema: string;
|
refSchema: string;
|
||||||
table: string;
|
table: string;
|
||||||
@ -147,15 +156,6 @@ export interface TableForeign {
|
|||||||
oldName?: string;
|
oldName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TableOptions {
|
|
||||||
name: string;
|
|
||||||
type?: 'table' | 'view';
|
|
||||||
engine?: string;
|
|
||||||
comment?: string;
|
|
||||||
collation?: string;
|
|
||||||
autoIncrement?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CreateTableParams {
|
export interface CreateTableParams {
|
||||||
/** Connection UID */
|
/** Connection UID */
|
||||||
uid?: string;
|
uid?: string;
|
||||||
@ -236,15 +236,12 @@ export interface CreateTriggerParams {
|
|||||||
export interface AlterTriggerParams extends CreateTriggerParams {
|
export interface AlterTriggerParams extends CreateTriggerParams {
|
||||||
oldName?: string;
|
oldName?: string;
|
||||||
}
|
}
|
||||||
export interface TriggerFunctionInfos {
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
security: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Routines & Functions
|
// Routines & Functions
|
||||||
|
|
||||||
export interface FunctionParam {
|
export interface FunctionParam {
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
_antares_id: string;
|
||||||
context: string;
|
context: string;
|
||||||
name: string;
|
name: string;
|
||||||
type: string;
|
type: string;
|
||||||
@ -267,9 +264,11 @@ export interface RoutineInfos {
|
|||||||
parameters?: FunctionParam[];
|
parameters?: FunctionParam[];
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
returns?: any;
|
returns?: any;
|
||||||
|
returnsLength?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FunctionInfos = RoutineInfos
|
export type FunctionInfos = RoutineInfos
|
||||||
|
export type TriggerFunctionInfos = FunctionInfos
|
||||||
|
|
||||||
export interface CreateRoutineParams {
|
export interface CreateRoutineParams {
|
||||||
name: string;
|
name: string;
|
||||||
@ -309,29 +308,6 @@ export interface AlterFunctionParams extends CreateFunctionParams {
|
|||||||
|
|
||||||
// Events
|
// Events
|
||||||
export interface EventInfos {
|
export interface EventInfos {
|
||||||
name: string;
|
|
||||||
definition: string;
|
|
||||||
type: string;
|
|
||||||
definer: string;
|
|
||||||
body: string;
|
|
||||||
starts: string;
|
|
||||||
ends: string;
|
|
||||||
enabled: boolean;
|
|
||||||
executeAt: string;
|
|
||||||
intervalField: string;
|
|
||||||
intervalValue: string;
|
|
||||||
onCompletion: string;
|
|
||||||
originator: string;
|
|
||||||
sqlMode: string;
|
|
||||||
created: string;
|
|
||||||
updated: string;
|
|
||||||
lastExecuted: string;
|
|
||||||
comment: string;
|
|
||||||
charset: string;
|
|
||||||
timezone: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CreateEventParams {
|
|
||||||
definer?: string;
|
definer?: string;
|
||||||
schema: string;
|
schema: string;
|
||||||
name: string;
|
name: string;
|
||||||
@ -340,12 +316,15 @@ export interface CreateEventParams {
|
|||||||
starts: string;
|
starts: string;
|
||||||
ends: string;
|
ends: string;
|
||||||
at: string;
|
at: string;
|
||||||
preserve: string;
|
preserve: boolean;
|
||||||
state: string;
|
state: string;
|
||||||
comment: string;
|
comment: string;
|
||||||
|
enabled?: boolean;
|
||||||
sql: string;
|
sql: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type CreateEventParams = EventInfos;
|
||||||
|
|
||||||
export interface AlterEventParams extends CreateEventParams {
|
export interface AlterEventParams extends CreateEventParams {
|
||||||
oldName?: string;
|
oldName?: string;
|
||||||
}
|
}
|
||||||
@ -397,17 +376,10 @@ export interface QueryParams {
|
|||||||
tabUid?: string;
|
tabUid?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface QueryField {
|
/**
|
||||||
name: string;
|
* @deprecated Use TableFIeld
|
||||||
alias: string;
|
*/
|
||||||
orgName: string;
|
export type QueryField = TableField
|
||||||
schema: string;
|
|
||||||
table: string;
|
|
||||||
tableAlias: string;
|
|
||||||
orgTable: string;
|
|
||||||
type: string;
|
|
||||||
length: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface QueryForeign {
|
export interface QueryForeign {
|
||||||
schema: string;
|
schema: string;
|
||||||
|
@ -1,5 +1,34 @@
|
|||||||
import { UsableLocale } from '@faker-js/faker';
|
import { UsableLocale } from '@faker-js/faker';
|
||||||
|
|
||||||
|
export interface TableUpdateParams {
|
||||||
|
uid: string;
|
||||||
|
schema: string;
|
||||||
|
table: string;
|
||||||
|
primary?: string;
|
||||||
|
id: number | string;
|
||||||
|
content: number | string | boolean | Date | Blob | null;
|
||||||
|
type: string;
|
||||||
|
field: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TableDeleteParams {
|
||||||
|
uid: string;
|
||||||
|
schema: string;
|
||||||
|
table: string;
|
||||||
|
primary?: string;
|
||||||
|
field: string;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
rows: {[key: string]: any};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TableFilterClausole {
|
||||||
|
active: boolean;
|
||||||
|
field: string;
|
||||||
|
op: '=' | '!=' | '>' | '<' | '>=' | '<=' | 'IN' | 'NOT IN' | 'LIKE' | 'BETWEEN' | 'IS NULL' | 'IS NOT NULL';
|
||||||
|
value: '';
|
||||||
|
value2: '';
|
||||||
|
}
|
||||||
|
|
||||||
export interface InsertRowsParams {
|
export interface InsertRowsParams {
|
||||||
uid: string;
|
uid: string;
|
||||||
schema: string;
|
schema: string;
|
||||||
|
@ -381,25 +381,18 @@ export class MySQLClient extends AntaresCore {
|
|||||||
const remappedSchedulers: antares.EventInfos[] = schedulers.filter(scheduler => scheduler.Db === db.Database).map(scheduler => {
|
const remappedSchedulers: antares.EventInfos[] = schedulers.filter(scheduler => scheduler.Db === db.Database).map(scheduler => {
|
||||||
return {
|
return {
|
||||||
name: scheduler.EVENT_NAME,
|
name: scheduler.EVENT_NAME,
|
||||||
definition: scheduler.EVENT_DEFINITION,
|
schema: scheduler.Db,
|
||||||
type: scheduler.EVENT_TYPE,
|
sql: scheduler.EVENT_DEFINITION,
|
||||||
|
execution: scheduler.EVENT_TYPE === 'RECURRING' ? 'EVERY' : 'ONCE',
|
||||||
definer: scheduler.DEFINER,
|
definer: scheduler.DEFINER,
|
||||||
body: scheduler.EVENT_BODY,
|
|
||||||
starts: scheduler.STARTS,
|
starts: scheduler.STARTS,
|
||||||
ends: scheduler.ENDS,
|
ends: scheduler.ENDS,
|
||||||
|
state: scheduler.STATUS === 'ENABLED' ? 'ENABLE' : scheduler.STATE === 'DISABLED' ? 'DISABLE' : 'DISABLE ON SLAVE',
|
||||||
enabled: scheduler.STATUS === 'ENABLED',
|
enabled: scheduler.STATUS === 'ENABLED',
|
||||||
executeAt: scheduler.EXECUTE_AT,
|
at: scheduler.EXECUTE_AT,
|
||||||
intervalField: scheduler.INTERVAL_FIELD,
|
every: [scheduler.INTERVAL_FIELD, scheduler.INTERVAL_VALUE],
|
||||||
intervalValue: scheduler.INTERVAL_VALUE,
|
preserve: scheduler.ON_COMPLETION.includes('PRESERVE'),
|
||||||
onCompletion: scheduler.ON_COMPLETION,
|
comment: scheduler.EVENT_COMMENT
|
||||||
originator: scheduler.ORIGINATOR,
|
|
||||||
sqlMode: scheduler.SQL_MODE,
|
|
||||||
created: scheduler.CREATED,
|
|
||||||
updated: scheduler.LAST_ALTERED,
|
|
||||||
lastExecuted: scheduler.LAST_EXECUTED,
|
|
||||||
comment: scheduler.EVENT_COMMENT,
|
|
||||||
charset: scheduler.CHARACTER_SET_CLIENT,
|
|
||||||
timezone: scheduler.TIME_ZONE
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -930,19 +923,22 @@ export class MySQLClient extends AntaresCore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getViewInformations ({ schema, view }: { schema: string; view: string }) {
|
async getViewInformations ({ schema, view }: { schema: string; view: string }) {
|
||||||
const sql = `SHOW CREATE VIEW \`${schema}\`.\`${view}\``;
|
const { rows: algorithm } = await this.raw(`SHOW CREATE VIEW \`${schema}\`.\`${view}\``);
|
||||||
const results = await this.raw(sql);
|
const { rows: viewInfo } = await this.raw(`
|
||||||
|
SELECT *
|
||||||
|
FROM INFORMATION_SCHEMA.VIEWS
|
||||||
|
WHERE TABLE_SCHEMA = '${schema}'
|
||||||
|
AND TABLE_NAME = '${view}'
|
||||||
|
`);
|
||||||
|
|
||||||
return results.rows.map(row => {
|
|
||||||
return {
|
return {
|
||||||
algorithm: row['Create View'].match(/(?<=CREATE ALGORITHM=).*?(?=\s)/gs)[0],
|
algorithm: algorithm[0]['Create View'].match(/(?<=CREATE ALGORITHM=).*?(?=\s)/gs)[0],
|
||||||
definer: row['Create View'].match(/(?<=DEFINER=).*?(?=\s)/gs)[0],
|
definer: viewInfo[0].DEFINER,
|
||||||
security: row['Create View'].match(/(?<=SQL SECURITY ).*?(?=\s)/gs)[0],
|
security: viewInfo[0].SECURITY_TYPE,
|
||||||
updateOption: row['Create View'].match(/(?<=WITH ).*?(?=\s)/gs) ? row['Create View'].match(/(?<=WITH ).*?(?=\s)/gs)[0] : '',
|
updateOption: viewInfo[0].CHECK_OPTION === 'NONE' ? '' : viewInfo[0].CHECK_OPTION,
|
||||||
sql: row['Create View'].match(/(?<=AS ).*?$/gs)[0],
|
sql: viewInfo[0].VIEW_DEFINITION,
|
||||||
name: row.View
|
name: viewInfo[0].TABLE_NAME
|
||||||
};
|
};
|
||||||
})[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async dropView (params: { schema: string; view: string }) {
|
async dropView (params: { schema: string; view: string }) {
|
||||||
@ -955,7 +951,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
USE \`${view.schema}\`;
|
USE \`${view.schema}\`;
|
||||||
ALTER ALGORITHM = ${view.algorithm}${view.definer ? ` DEFINER=${view.definer}` : ''}
|
ALTER ALGORITHM = ${view.algorithm}${view.definer ? ` DEFINER=${view.definer}` : ''}
|
||||||
SQL SECURITY ${view.security}
|
SQL SECURITY ${view.security}
|
||||||
params \`${view.schema}\`.\`${view.oldName}\` AS ${view.sql} ${view.updateOption ? `WITH ${view.updateOption} CHECK OPTION` : ''}
|
VIEW \`${view.schema}\`.\`${view.oldName}\` AS ${view.sql} ${view.updateOption ? `WITH ${view.updateOption} CHECK OPTION` : ''}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
if (view.name !== view.oldName)
|
if (view.name !== view.oldName)
|
||||||
|
@ -50,14 +50,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, PropType, Ref, ref } from 'vue';
|
import { computed, PropType, Ref, ref } from 'vue';
|
||||||
import { NUMBER, FLOAT } from 'common/fieldTypes';
|
import { NUMBER, FLOAT } from 'common/fieldTypes';
|
||||||
import { FunctionParam } from 'common/interfaces/antares';
|
import { FunctionInfos, RoutineInfos } from 'common/interfaces/antares';
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal.vue';
|
import ConfirmModal from '@/components/BaseConfirmModal.vue';
|
||||||
|
|
||||||
// eslint-disable-next-line camelcase
|
|
||||||
type LocalRoutineParams = FunctionParam & {_antares_id: string};
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
localRoutine: Object as PropType<{name: string; parameters: LocalRoutineParams[]}>,
|
localRoutine: Object as PropType<RoutineInfos | FunctionInfos>,
|
||||||
client: String
|
client: String
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<div class="modal-title h6">
|
<div class="modal-title h6">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-history mr-1" />
|
<i class="mdi mdi-24px mdi-history mr-1" />
|
||||||
<span class="cut-text">{{ $t('word.history') }}: {{ connectionName }}</span>
|
<span class="cut-text">{{ t('word.history') }}: {{ connectionName }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||||
@ -22,7 +22,7 @@
|
|||||||
v-model="searchTerm"
|
v-model="searchTerm"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
type="text"
|
type="text"
|
||||||
:placeholder="$t('message.searchForQueries')"
|
:placeholder="t('message.searchForQueries')"
|
||||||
>
|
>
|
||||||
<i v-if="!searchTerm" class="form-icon mdi mdi-magnify mdi-18px pr-4" />
|
<i v-if="!searchTerm" class="form-icon mdi mdi-magnify mdi-18px pr-4" />
|
||||||
<i
|
<i
|
||||||
@ -67,13 +67,13 @@
|
|||||||
<small class="tile-subtitle">{{ query.schema }} · {{ formatDate(query.date) }}</small>
|
<small class="tile-subtitle">{{ query.schema }} · {{ formatDate(query.date) }}</small>
|
||||||
<div class="tile-history-buttons">
|
<div class="tile-history-buttons">
|
||||||
<button class="btn btn-link pl-1" @click.stop="$emit('select-query', query.sql)">
|
<button class="btn btn-link pl-1" @click.stop="$emit('select-query', query.sql)">
|
||||||
<i class="mdi mdi-open-in-app pr-1" /> {{ $t('word.select') }}
|
<i class="mdi mdi-open-in-app pr-1" /> {{ t('word.select') }}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-link pl-1" @click="copyQuery(query.sql)">
|
<button class="btn btn-link pl-1" @click="copyQuery(query.sql)">
|
||||||
<i class="mdi mdi-content-copy pr-1" /> {{ $t('word.copy') }}
|
<i class="mdi mdi-content-copy pr-1" /> {{ t('word.copy') }}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-link pl-1" @click="deleteQuery(query)">
|
<button class="btn btn-link pl-1" @click="deleteQuery(query)">
|
||||||
<i class="mdi mdi-delete-forever pr-1" /> {{ $t('word.delete') }}
|
<i class="mdi mdi-delete-forever pr-1" /> {{ t('word.delete') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -88,7 +88,7 @@
|
|||||||
<i class="mdi mdi-history mdi-48px" />
|
<i class="mdi mdi-history mdi-48px" />
|
||||||
</div>
|
</div>
|
||||||
<p class="empty-title h5">
|
<p class="empty-title h5">
|
||||||
{{ $t('message.thereIsNoQueriesYet') }}
|
{{ t('message.thereIsNoQueriesYet') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -99,12 +99,15 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Component, computed, ComputedRef, onBeforeUnmount, onMounted, onUpdated, Prop, Ref, ref, watch } from 'vue';
|
import { Component, computed, ComputedRef, onBeforeUnmount, onMounted, onUpdated, Prop, Ref, ref, watch } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
import { ConnectionParams } from 'common/interfaces/antares';
|
import { ConnectionParams } from 'common/interfaces/antares';
|
||||||
import { HistoryRecord, useHistoryStore } from '@/stores/history';
|
import { HistoryRecord, useHistoryStore } from '@/stores/history';
|
||||||
import { useConnectionsStore } from '@/stores/connections';
|
import { useConnectionsStore } from '@/stores/connections';
|
||||||
import BaseVirtualScroll from '@/components/BaseVirtualScroll.vue';
|
import BaseVirtualScroll from '@/components/BaseVirtualScroll.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const { getHistoryByWorkspace, deleteQueryFromHistory } = useHistoryStore();
|
const { getHistoryByWorkspace, deleteQueryFromHistory } = useHistoryStore();
|
||||||
const { getConnectionName } = useConnectionsStore();
|
const { getConnectionName } = useConnectionsStore();
|
||||||
|
|
||||||
|
@ -11,27 +11,27 @@
|
|||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
<span>{{ $t('word.save') }}</span>
|
<span>{{ t('word.save') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
class="btn btn-link btn-sm mr-0"
|
class="btn btn-link btn-sm mr-0"
|
||||||
:title="$t('message.clearChanges')"
|
:title="t('message.clearChanges')"
|
||||||
@click="clearChanges"
|
@click="clearChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
||||||
<span>{{ $t('word.clear') }}</span>
|
<span>{{ t('word.clear') }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="divider-vert py-3" />
|
<div class="divider-vert py-3" />
|
||||||
|
|
||||||
<button class="btn btn-dark btn-sm" @click="showParamsModal">
|
<button class="btn btn-dark btn-sm" @click="showParamsModal">
|
||||||
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" />
|
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" />
|
||||||
<span>{{ $t('word.parameters') }}</span>
|
<span>{{ t('word.parameters') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="workspace-query-info">
|
<div class="workspace-query-info">
|
||||||
<div class="d-flex" :title="$t('word.schema')">
|
<div class="d-flex" :title="t('word.schema')">
|
||||||
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
|
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -42,7 +42,7 @@
|
|||||||
<div class="column col-auto">
|
<div class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('word.name') }}
|
{{ t('word.name') }}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
ref="firstInput"
|
ref="firstInput"
|
||||||
@ -55,7 +55,7 @@
|
|||||||
<div v-if="customizations.languages" class="column col-auto">
|
<div v-if="customizations.languages" class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('word.language') }}
|
{{ t('word.language') }}
|
||||||
</label>
|
</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localFunction.language"
|
v-model="localFunction.language"
|
||||||
@ -67,11 +67,11 @@
|
|||||||
<div v-if="customizations.definer" class="column col-auto">
|
<div v-if="customizations.definer" class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('word.definer') }}
|
{{ t('word.definer') }}
|
||||||
</label>
|
</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localFunction.definer"
|
v-model="localFunction.definer"
|
||||||
:options="[{value: '', name:$t('message.currentUser')}, ...workspace.users]"
|
:options="[{value: '', name:t('message.currentUser')}, ...workspace.users]"
|
||||||
:option-label="(user: any) => user.value === '' ? user.name : `${user.name}@${user.host}`"
|
:option-label="(user: any) => user.value === '' ? user.name : `${user.name}@${user.host}`"
|
||||||
:option-track-by="(user: any) => user.value === '' ? '' : `\`${user.name}\`@\`${user.host}\``"
|
:option-track-by="(user: any) => user.value === '' ? '' : `\`${user.name}\`@\`${user.host}\``"
|
||||||
class="form-select"
|
class="form-select"
|
||||||
@ -81,7 +81,7 @@
|
|||||||
<div class="column col-auto">
|
<div class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('word.returns') }}
|
{{ t('word.returns') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
@ -101,7 +101,7 @@
|
|||||||
class="form-input"
|
class="form-input"
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
:placeholder="$t('word.length')"
|
:placeholder="t('word.length')"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -109,7 +109,7 @@
|
|||||||
<div v-if="customizations.comment" class="column">
|
<div v-if="customizations.comment" class="column">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('word.comment') }}
|
{{ t('word.comment') }}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
v-model="localFunction.comment"
|
v-model="localFunction.comment"
|
||||||
@ -121,7 +121,7 @@
|
|||||||
<div class="column col-auto">
|
<div class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('message.sqlSecurity') }}
|
{{ t('message.sqlSecurity') }}
|
||||||
</label>
|
</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localFunction.security"
|
v-model="localFunction.security"
|
||||||
@ -133,7 +133,7 @@
|
|||||||
<div v-if="customizations.functionDataAccess" class="column col-auto">
|
<div v-if="customizations.functionDataAccess" class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('message.dataAccess') }}
|
{{ t('message.dataAccess') }}
|
||||||
</label>
|
</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localFunction.dataAccess"
|
v-model="localFunction.dataAccess"
|
||||||
@ -146,7 +146,7 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label d-invisible">.</label>
|
<label class="form-label d-invisible">.</label>
|
||||||
<label class="form-checkbox form-inline">
|
<label class="form-checkbox form-inline">
|
||||||
<input v-model="localFunction.deterministic" type="checkbox"><i class="form-icon" /> {{ $t('word.deterministic') }}
|
<input v-model="localFunction.deterministic" type="checkbox"><i class="form-icon" /> {{ t('word.deterministic') }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -154,7 +154,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
||||||
<BaseLoader v-if="isLoading" />
|
<BaseLoader v-if="isLoading" />
|
||||||
<label class="form-label ml-2">{{ $t('message.functionBody') }}</label>
|
<label class="form-label ml-2">{{ t('message.functionBody') }}</label>
|
||||||
<QueryEditor
|
<QueryEditor
|
||||||
v-show="isSelected"
|
v-show="isSelected"
|
||||||
ref="queryEditor"
|
ref="queryEditor"
|
||||||
@ -186,6 +186,9 @@ import QueryEditor from '@/components/QueryEditor.vue';
|
|||||||
import WorkspaceTabPropsFunctionParamsModal from '@/components/WorkspaceTabPropsFunctionParamsModal.vue';
|
import WorkspaceTabPropsFunctionParamsModal from '@/components/WorkspaceTabPropsFunctionParamsModal.vue';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
import { FunctionInfos, FunctionParam } from 'common/interfaces/antares';
|
import { FunctionInfos, FunctionParam } from 'common/interfaces/antares';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
tabUid: String,
|
tabUid: String,
|
||||||
|
@ -201,7 +201,7 @@ const {
|
|||||||
changeBreadcrumbs
|
changeBreadcrumbs
|
||||||
} = workspacesStore;
|
} = workspacesStore;
|
||||||
|
|
||||||
const indexTable: Ref<Component & {$refs: { tableWrapper: HTMLDivElement }}> = ref(null);
|
const indexTable: Ref<Component & { tableWrapper: HTMLDivElement }> = ref(null);
|
||||||
const firstInput: Ref<HTMLInputElement> = ref(null);
|
const firstInput: Ref<HTMLInputElement> = ref(null);
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
const isSaving = ref(false);
|
const isSaving = ref(false);
|
||||||
@ -336,7 +336,7 @@ const addField = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const scrollable = indexTable.value.$refs.tableWrapper;
|
const scrollable = indexTable.value.tableWrapper;
|
||||||
scrollable.scrollTop = scrollable.scrollHeight + 30;
|
scrollable.scrollTop = scrollable.scrollHeight + 30;
|
||||||
}, 20);
|
}, 20);
|
||||||
};
|
};
|
||||||
@ -364,7 +364,7 @@ const duplicateField = (uid: string) => {
|
|||||||
localFields.value = [...localFields.value, fieldToClone];
|
localFields.value = [...localFields.value, fieldToClone];
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const scrollable = indexTable.value.$refs.tableWrapper;
|
const scrollable = indexTable.value.tableWrapper;
|
||||||
scrollable.scrollTop = scrollable.scrollHeight + 30;
|
scrollable.scrollTop = scrollable.scrollHeight + 30;
|
||||||
}, 20);
|
}, 20);
|
||||||
};
|
};
|
||||||
|
@ -85,12 +85,12 @@
|
|||||||
/>
|
/>
|
||||||
<div v-if="customizations.triggerMultipleEvents" class="px-4">
|
<div v-if="customizations.triggerMultipleEvents" class="px-4">
|
||||||
<label
|
<label
|
||||||
v-for="event in Object.keys(localEvents)"
|
v-for="event in Object.keys(localEvents) as ('INSERT' | 'UPDATE' | 'DELETE')[]"
|
||||||
:key="event"
|
:key="event"
|
||||||
class="form-checkbox form-inline"
|
class="form-checkbox form-inline"
|
||||||
@change.prevent="changeEvents(event as 'INSERT' | 'UPDATE' | 'DELETE')"
|
@change.prevent="changeEvents(event)"
|
||||||
>
|
>
|
||||||
<input :checked="localEvents[event as 'INSERT' | 'UPDATE' | 'DELETE']" type="checkbox"><i class="form-icon" /> {{ event }}
|
<input :checked="localEvents[event]" type="checkbox"><i class="form-icon" /> {{ event }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -144,17 +144,9 @@ const originalView = ref(null);
|
|||||||
const localView = ref(null);
|
const localView = ref(null);
|
||||||
const editorHeight = ref(300);
|
const editorHeight = ref(300);
|
||||||
|
|
||||||
const workspace = computed(() => {
|
const workspace = computed(() => getWorkspace(props.connection.uid));
|
||||||
return getWorkspace(props.connection.uid);
|
const isChanged = computed(() => JSON.stringify(originalView.value) !== JSON.stringify(localView.value));
|
||||||
});
|
const isDefinerInUsers = computed(() => originalView.value ? workspace.value.users.some(user => originalView.value.definer === `\`${user.name}\`@\`${user.host}\``) : true);
|
||||||
|
|
||||||
const isChanged = computed(() => {
|
|
||||||
return JSON.stringify(originalView.value) !== JSON.stringify(localView.value);
|
|
||||||
});
|
|
||||||
|
|
||||||
const isDefinerInUsers = computed(() => {
|
|
||||||
return originalView.value ? workspace.value.users.some(user => originalView.value.definer === `\`${user.name}\`@\`${user.host}\``) : true;
|
|
||||||
});
|
|
||||||
|
|
||||||
const users = computed(() => {
|
const users = computed(() => {
|
||||||
const users = [{ value: '' }, ...workspace.value.users];
|
const users = [{ value: '' }, ...workspace.value.users];
|
||||||
|
@ -11,16 +11,16 @@
|
|||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
<span>{{ $t('word.save') }}</span>
|
<span>{{ t('word.save') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
class="btn btn-link btn-sm mr-0"
|
class="btn btn-link btn-sm mr-0"
|
||||||
:title="$t('message.clearChanges')"
|
:title="t('message.clearChanges')"
|
||||||
@click="clearChanges"
|
@click="clearChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
||||||
<span>{{ $t('word.clear') }}</span>
|
<span>{{ t('word.clear') }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="divider-vert py-3" />
|
<div class="divider-vert py-3" />
|
||||||
@ -31,15 +31,15 @@
|
|||||||
@click="runFunctionCheck"
|
@click="runFunctionCheck"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-play mr-1" />
|
<i class="mdi mdi-24px mdi-play mr-1" />
|
||||||
<span>{{ $t('word.run') }}</span>
|
<span>{{ t('word.run') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-dark btn-sm" @click="showParamsModal">
|
<button class="btn btn-dark btn-sm" @click="showParamsModal">
|
||||||
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" />
|
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" />
|
||||||
<span>{{ $t('word.parameters') }}</span>
|
<span>{{ t('word.parameters') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="workspace-query-info">
|
<div class="workspace-query-info">
|
||||||
<div class="d-flex" :title="$t('word.schema')">
|
<div class="d-flex" :title="t('word.schema')">
|
||||||
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
|
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -50,7 +50,7 @@
|
|||||||
<div class="column col-auto">
|
<div class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('word.name') }}
|
{{ t('word.name') }}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
ref="firstInput"
|
ref="firstInput"
|
||||||
@ -64,7 +64,7 @@
|
|||||||
<div v-if="customizations.languages" class="column col-auto">
|
<div v-if="customizations.languages" class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('word.language') }}
|
{{ t('word.language') }}
|
||||||
</label>
|
</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localFunction.language"
|
v-model="localFunction.language"
|
||||||
@ -76,13 +76,13 @@
|
|||||||
<div v-if="customizations.definer" class="column col-auto">
|
<div v-if="customizations.definer" class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('word.definer') }}
|
{{ t('word.definer') }}
|
||||||
</label>
|
</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localFunction.definer"
|
v-model="localFunction.definer"
|
||||||
:options="[{value: '', name:$t('message.currentUser')}, ...workspace.users]"
|
:options="[{value: '', name:t('message.currentUser')}, ...workspace.users]"
|
||||||
:option-label="(user) => user.value === '' ? user.name : `${user.name}@${user.host}`"
|
:option-label="(user: any) => user.value === '' ? user.name : `${user.name}@${user.host}`"
|
||||||
:option-track-by="(user) => user.value === '' ? '' : `\`${user.name}\`@\`${user.host}\``"
|
:option-track-by="(user: any) => user.value === '' ? '' : `\`${user.name}\`@\`${user.host}\``"
|
||||||
class="form-select"
|
class="form-select"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -90,13 +90,13 @@
|
|||||||
<div class="column col-auto">
|
<div class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('word.returns') }}
|
{{ t('word.returns') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localFunction.returns"
|
v-model="localFunction.returns"
|
||||||
class="form-select text-uppercase"
|
class="form-select text-uppercase"
|
||||||
:options="[{ name: 'VOID' }, ...workspace.dataTypes]"
|
:options="[{ name: 'VOID' }, ...(workspace.dataTypes as any)]"
|
||||||
group-label="group"
|
group-label="group"
|
||||||
group-values="types"
|
group-values="types"
|
||||||
option-label="name"
|
option-label="name"
|
||||||
@ -110,7 +110,7 @@
|
|||||||
class="form-input"
|
class="form-input"
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
:placeholder="$t('word.length')"
|
:placeholder="t('word.length')"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -118,7 +118,7 @@
|
|||||||
<div v-if="customizations.comment" class="column">
|
<div v-if="customizations.comment" class="column">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('word.comment') }}
|
{{ t('word.comment') }}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
v-model="localFunction.comment"
|
v-model="localFunction.comment"
|
||||||
@ -130,7 +130,7 @@
|
|||||||
<div class="column col-auto">
|
<div class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('message.sqlSecurity') }}
|
{{ t('message.sqlSecurity') }}
|
||||||
</label>
|
</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localFunction.security"
|
v-model="localFunction.security"
|
||||||
@ -142,7 +142,7 @@
|
|||||||
<div v-if="customizations.functionDataAccess" class="column col-auto">
|
<div v-if="customizations.functionDataAccess" class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('message.dataAccess') }}
|
{{ t('message.dataAccess') }}
|
||||||
</label>
|
</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localFunction.dataAccess"
|
v-model="localFunction.dataAccess"
|
||||||
@ -155,7 +155,7 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label d-invisible">.</label>
|
<label class="form-label d-invisible">.</label>
|
||||||
<label class="form-checkbox form-inline">
|
<label class="form-checkbox form-inline">
|
||||||
<input v-model="localFunction.deterministic" type="checkbox"><i class="form-icon" /> {{ $t('word.deterministic') }}
|
<input v-model="localFunction.deterministic" type="checkbox"><i class="form-icon" /> {{ t('word.deterministic') }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -163,7 +163,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
||||||
<BaseLoader v-if="isLoading" />
|
<BaseLoader v-if="isLoading" />
|
||||||
<label class="form-label ml-2">{{ $t('message.functionBody') }}</label>
|
<label class="form-label ml-2">{{ t('message.functionBody') }}</label>
|
||||||
<QueryEditor
|
<QueryEditor
|
||||||
v-show="isSelected"
|
v-show="isSelected"
|
||||||
ref="queryEditor"
|
ref="queryEditor"
|
||||||
@ -191,40 +191,34 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import { storeToRefs } from 'pinia';
|
import { Component, computed, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch } from 'vue';
|
||||||
|
import { Ace } from 'ace-builds';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
import { useNotificationsStore } from '@/stores/notifications';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
import { uidGen } from 'common/libs/uidGen';
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
import BaseLoader from '@/components/BaseLoader';
|
import BaseLoader from '@/components/BaseLoader.vue';
|
||||||
import QueryEditor from '@/components/QueryEditor';
|
import QueryEditor from '@/components/QueryEditor.vue';
|
||||||
import WorkspaceTabPropsFunctionParamsModal from '@/components/WorkspaceTabPropsFunctionParamsModal';
|
import WorkspaceTabPropsFunctionParamsModal from '@/components/WorkspaceTabPropsFunctionParamsModal.vue';
|
||||||
import ModalAskParameters from '@/components/ModalAskParameters';
|
import ModalAskParameters from '@/components/ModalAskParameters.vue';
|
||||||
import Functions from '@/ipc-api/Functions';
|
import Functions from '@/ipc-api/Functions';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { AlterFunctionParams, FunctionInfos, FunctionParam } from 'common/interfaces/antares';
|
||||||
|
|
||||||
export default {
|
const { t } = useI18n();
|
||||||
name: 'WorkspaceTabPropsFunction',
|
|
||||||
components: {
|
const props = defineProps({
|
||||||
BaseLoader,
|
|
||||||
QueryEditor,
|
|
||||||
WorkspaceTabPropsFunctionParamsModal,
|
|
||||||
ModalAskParameters,
|
|
||||||
BaseSelect
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
tabUid: String,
|
tabUid: String,
|
||||||
connection: Object,
|
connection: Object,
|
||||||
function: String,
|
function: String,
|
||||||
isSelected: Boolean,
|
isSelected: Boolean,
|
||||||
schema: String
|
schema: String
|
||||||
},
|
});
|
||||||
setup () {
|
|
||||||
const { addNotification } = useNotificationsStore();
|
const { addNotification } = useNotificationsStore();
|
||||||
const workspacesStore = useWorkspacesStore();
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
|
||||||
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getWorkspace,
|
getWorkspace,
|
||||||
refreshStructure,
|
refreshStructure,
|
||||||
@ -234,257 +228,236 @@ export default {
|
|||||||
setUnsavedChanges
|
setUnsavedChanges
|
||||||
} = workspacesStore;
|
} = workspacesStore;
|
||||||
|
|
||||||
return {
|
const queryEditor: Ref<Component & {editor: Ace.Editor; $el: HTMLElement}> = ref(null);
|
||||||
addNotification,
|
const firstInput: Ref<HTMLInputElement> = ref(null);
|
||||||
selectedWorkspace,
|
const isLoading = ref(false);
|
||||||
getWorkspace,
|
const isSaving = ref(false);
|
||||||
refreshStructure,
|
const isParamsModal = ref(false);
|
||||||
renameTabs,
|
const isAskingParameters = ref(false);
|
||||||
newTab,
|
const originalFunction: Ref<FunctionInfos> = ref(null);
|
||||||
changeBreadcrumbs,
|
const localFunction: Ref<FunctionInfos> = ref({ name: '', sql: '', definer: null });
|
||||||
setUnsavedChanges
|
const lastFunction = ref(null);
|
||||||
};
|
const sqlProxy = ref('');
|
||||||
},
|
const editorHeight = ref(300);
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
isLoading: false,
|
|
||||||
isSaving: false,
|
|
||||||
isParamsModal: false,
|
|
||||||
isAskingParameters: false,
|
|
||||||
originalFunction: null,
|
|
||||||
localFunction: { sql: '' },
|
|
||||||
lastFunction: null,
|
|
||||||
sqlProxy: '',
|
|
||||||
editorHeight: 300
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
workspace () {
|
|
||||||
return this.getWorkspace(this.connection.uid);
|
|
||||||
},
|
|
||||||
customizations () {
|
|
||||||
return this.workspace.customizations;
|
|
||||||
},
|
|
||||||
isChanged () {
|
|
||||||
return JSON.stringify(this.originalFunction) !== JSON.stringify(this.localFunction);
|
|
||||||
},
|
|
||||||
isDefinerInUsers () {
|
|
||||||
return this.originalFunction
|
|
||||||
? this.workspace.users.some(user => this.originalFunction.definer === `\`${user.name}\`@\`${user.host}\``)
|
|
||||||
: true;
|
|
||||||
},
|
|
||||||
isTableNameValid () {
|
|
||||||
return this.localFunction.name !== '';
|
|
||||||
},
|
|
||||||
isInDataTypes () {
|
|
||||||
let typeNames = [];
|
|
||||||
for (const group of this.workspace.dataTypes) {
|
|
||||||
typeNames = group.types.reduce((acc, curr) => {
|
|
||||||
acc.push(curr.name);
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
}
|
|
||||||
return typeNames.includes(this.localFunction.returns);
|
|
||||||
},
|
|
||||||
schemaTables () {
|
|
||||||
const schemaTables = this.workspace.structure
|
|
||||||
.filter(schema => schema.name === this.schema)
|
|
||||||
.map(schema => schema.tables);
|
|
||||||
|
|
||||||
return schemaTables.length ? schemaTables[0].filter(table => table.type === 'table') : [];
|
const workspace = computed(() => {
|
||||||
}
|
return getWorkspace(props.connection.uid);
|
||||||
},
|
});
|
||||||
watch: {
|
|
||||||
async schema () {
|
|
||||||
if (this.isSelected) {
|
|
||||||
await this.getFunctionData();
|
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
|
|
||||||
this.lastFunction = this.function;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async function () {
|
|
||||||
if (this.isSelected) {
|
|
||||||
await this.getFunctionData();
|
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
|
|
||||||
this.lastFunction = this.function;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async isSelected (val) {
|
|
||||||
if (val) {
|
|
||||||
this.changeBreadcrumbs({ schema: this.schema, function: this.function });
|
|
||||||
|
|
||||||
setTimeout(() => {
|
const customizations = computed(() => {
|
||||||
this.resizeQueryEditor();
|
return workspace.value.customizations;
|
||||||
}, 200);
|
});
|
||||||
|
|
||||||
if (this.lastFunction !== this.function)
|
const isChanged = computed(() => {
|
||||||
this.getRoutineData();
|
return JSON.stringify(originalFunction.value) !== JSON.stringify(localFunction.value);
|
||||||
}
|
});
|
||||||
},
|
|
||||||
isChanged (val) {
|
|
||||||
this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async created () {
|
|
||||||
await this.getFunctionData();
|
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
|
|
||||||
window.addEventListener('keydown', this.onKey);
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
window.addEventListener('resize', this.resizeQueryEditor);
|
|
||||||
},
|
|
||||||
unmounted () {
|
|
||||||
window.removeEventListener('resize', this.resizeQueryEditor);
|
|
||||||
},
|
|
||||||
beforeUnmount () {
|
|
||||||
window.removeEventListener('keydown', this.onKey);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async getFunctionData () {
|
|
||||||
if (!this.function) return;
|
|
||||||
|
|
||||||
this.isLoading = true;
|
const isTableNameValid = computed(() => {
|
||||||
this.localFunction = { sql: '' };
|
return localFunction.value.name !== '';
|
||||||
this.lastFunction = this.function;
|
});
|
||||||
|
|
||||||
|
const getFunctionData = async () => {
|
||||||
|
if (!props.function) return;
|
||||||
|
|
||||||
|
isLoading.value = true;
|
||||||
|
localFunction.value = { name: '', sql: '', definer: null };
|
||||||
|
lastFunction.value = props.function;
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: props.connection.uid,
|
||||||
schema: this.schema,
|
schema: props.schema,
|
||||||
func: this.function
|
func: props.function
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { status, response } = await Functions.getFunctionInformations(params);
|
const { status, response } = await Functions.getFunctionInformations(params);
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
this.originalFunction = response;
|
originalFunction.value = response;
|
||||||
|
|
||||||
this.originalFunction.parameters = [...this.originalFunction.parameters.map(param => {
|
originalFunction.value.parameters = [...originalFunction.value.parameters.map(param => {
|
||||||
param._antares_id = uidGen();
|
param._antares_id = uidGen();
|
||||||
return param;
|
return param;
|
||||||
})];
|
})];
|
||||||
|
|
||||||
this.localFunction = JSON.parse(JSON.stringify(this.originalFunction));
|
localFunction.value = JSON.parse(JSON.stringify(originalFunction.value));
|
||||||
this.sqlProxy = this.localFunction.sql;
|
sqlProxy.value = localFunction.value.sql;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
addNotification({ status: 'error', message: response });
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.resizeQueryEditor();
|
resizeQueryEditor();
|
||||||
this.isLoading = false;
|
isLoading.value = false;
|
||||||
},
|
};
|
||||||
async saveChanges () {
|
|
||||||
if (this.isSaving) return;
|
const saveChanges = async () => {
|
||||||
this.isSaving = true;
|
if (isSaving.value) return;
|
||||||
|
isSaving.value = true;
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: props.connection.uid,
|
||||||
func: {
|
func: {
|
||||||
...this.localFunction,
|
...localFunction.value,
|
||||||
schema: this.schema,
|
schema: props.schema,
|
||||||
oldName: this.originalFunction.name
|
oldName: originalFunction.value.name
|
||||||
}
|
} as AlterFunctionParams
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { status, response } = await Functions.alterFunction(params);
|
const { status, response } = await Functions.alterFunction(params);
|
||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
const oldName = this.originalFunction.name;
|
const oldName = originalFunction.value.name;
|
||||||
|
|
||||||
await this.refreshStructure(this.connection.uid);
|
await refreshStructure(props.connection.uid);
|
||||||
|
|
||||||
if (oldName !== this.localFunction.name) {
|
if (oldName !== localFunction.value.name) {
|
||||||
this.renameTabs({
|
renameTabs({
|
||||||
uid: this.connection.uid,
|
uid: props.connection.uid,
|
||||||
schema: this.schema,
|
schema: props.schema,
|
||||||
elementName: oldName,
|
elementName: oldName,
|
||||||
elementNewName: this.localFunction.name,
|
elementNewName: localFunction.value.name,
|
||||||
elementType: 'function'
|
elementType: 'function'
|
||||||
});
|
});
|
||||||
|
|
||||||
this.changeBreadcrumbs({ schema: this.schema, function: this.localFunction.name });
|
changeBreadcrumbs({ schema: props.schema, function: localFunction.value.name });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.getFunctionData();
|
getFunctionData();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
addNotification({ status: 'error', message: response });
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isSaving = false;
|
isSaving.value = false;
|
||||||
},
|
};
|
||||||
clearChanges () {
|
|
||||||
this.localFunction = JSON.parse(JSON.stringify(this.originalFunction));
|
const clearChanges = () => {
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
|
localFunction.value = JSON.parse(JSON.stringify(originalFunction.value));
|
||||||
},
|
queryEditor.value.editor.session.setValue(localFunction.value.sql);
|
||||||
resizeQueryEditor () {
|
};
|
||||||
if (this.$refs.queryEditor) {
|
|
||||||
|
const resizeQueryEditor = () => {
|
||||||
|
if (queryEditor.value) {
|
||||||
const footer = document.getElementById('footer');
|
const footer = document.getElementById('footer');
|
||||||
const size = window.innerHeight - this.$refs.queryEditor.$el.getBoundingClientRect().top - footer.offsetHeight;
|
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - footer.offsetHeight;
|
||||||
this.editorHeight = size;
|
editorHeight.value = size;
|
||||||
this.$refs.queryEditor.editor.resize();
|
queryEditor.value.editor.resize();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
optionsUpdate (options) {
|
|
||||||
this.localFunction = options;
|
const parametersUpdate = (parameters: FunctionParam[]) => {
|
||||||
},
|
localFunction.value = { ...localFunction.value, parameters };
|
||||||
parametersUpdate (parameters) {
|
};
|
||||||
this.localFunction = { ...this.localFunction, parameters };
|
|
||||||
},
|
const runFunctionCheck = () => {
|
||||||
runFunctionCheck () {
|
if (localFunction.value.parameters.length)
|
||||||
if (this.localFunction.parameters.length)
|
showAskParamsModal();
|
||||||
this.showAskParamsModal();
|
|
||||||
else
|
else
|
||||||
this.runFunction();
|
runFunction();
|
||||||
},
|
};
|
||||||
runFunction (params) {
|
|
||||||
|
const runFunction = (params?: string[]) => {
|
||||||
if (!params) params = [];
|
if (!params) params = [];
|
||||||
|
|
||||||
let sql;
|
let sql;
|
||||||
switch (this.connection.client) { // TODO: move in a better place
|
switch (props.connection.client) { // TODO: move in a better place
|
||||||
case 'maria':
|
case 'maria':
|
||||||
case 'mysql':
|
case 'mysql':
|
||||||
sql = `SELECT \`${this.originalFunction.name}\` (${params.join(',')})`;
|
sql = `SELECT \`${originalFunction.value.name}\` (${params.join(',')})`;
|
||||||
break;
|
break;
|
||||||
case 'pg':
|
case 'pg':
|
||||||
sql = `SELECT ${this.originalFunction.name}(${params.join(',')})`;
|
sql = `SELECT ${originalFunction.value.name}(${params.join(',')})`;
|
||||||
break;
|
break;
|
||||||
case 'mssql':
|
case 'mssql':
|
||||||
sql = `SELECT ${this.originalFunction.name} ${params.join(',')}`;
|
sql = `SELECT ${originalFunction.value.name} ${params.join(',')}`;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
sql = `SELECT \`${this.originalFunction.name}\` (${params.join(',')})`;
|
sql = `SELECT \`${originalFunction.value.name}\` (${params.join(',')})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.newTab({ uid: this.connection.uid, content: sql, type: 'query', autorun: true });
|
newTab({ uid: props.connection.uid, content: sql, type: 'query', autorun: true });
|
||||||
},
|
};
|
||||||
showParamsModal () {
|
|
||||||
this.isParamsModal = true;
|
const showParamsModal = () => {
|
||||||
},
|
isParamsModal.value = true;
|
||||||
hideParamsModal () {
|
};
|
||||||
this.isParamsModal = false;
|
|
||||||
},
|
const hideParamsModal = () => {
|
||||||
showAskParamsModal () {
|
isParamsModal.value = false;
|
||||||
this.isAskingParameters = true;
|
};
|
||||||
},
|
|
||||||
hideAskParamsModal () {
|
const showAskParamsModal = () => {
|
||||||
this.isAskingParameters = false;
|
isAskingParameters.value = true;
|
||||||
},
|
};
|
||||||
onKey (e) {
|
|
||||||
if (this.isSelected) {
|
const hideAskParamsModal = () => {
|
||||||
|
isAskingParameters.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onKey = (e: KeyboardEvent) => {
|
||||||
|
if (props.isSelected) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (e.ctrlKey && e.keyCode === 83) { // CTRL + S
|
if (e.ctrlKey && e.keyCode === 83) { // CTRL + S
|
||||||
if (this.isChanged)
|
if (isChanged.value)
|
||||||
this.saveChanges();
|
saveChanges();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(() => props.schema, async () => {
|
||||||
|
if (props.isSelected) {
|
||||||
|
await getFunctionData();
|
||||||
|
queryEditor.value.editor.session.setValue(localFunction.value.sql);
|
||||||
|
lastFunction.value = props.function;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => props.function, async () => {
|
||||||
|
if (props.isSelected) {
|
||||||
|
await getFunctionData();
|
||||||
|
queryEditor.value.editor.session.setValue(localFunction.value.sql);
|
||||||
|
lastFunction.value = props.function;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => props.isSelected, async (val) => {
|
||||||
|
if (val) {
|
||||||
|
changeBreadcrumbs({ schema: props.schema, function: props.function });
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
resizeQueryEditor();
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
if (lastFunction.value !== props.function)
|
||||||
|
getFunctionData();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(isChanged, (val) => {
|
||||||
|
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: val });
|
||||||
|
});
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
await getFunctionData();
|
||||||
|
queryEditor.value.editor.session.setValue(localFunction.value.sql);
|
||||||
|
window.addEventListener('keydown', onKey);
|
||||||
|
})();
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('resize', resizeQueryEditor);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('resize', resizeQueryEditor);
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.removeEventListener('keydown', onKey);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
:confirm-text="$t('word.confirm')"
|
:confirm-text="t('word.confirm')"
|
||||||
size="medium"
|
size="medium"
|
||||||
class="options-modal"
|
class="options-modal"
|
||||||
@confirm="confirmParametersChange"
|
@confirm="confirmParametersChange"
|
||||||
@ -9,7 +9,7 @@
|
|||||||
<template #header>
|
<template #header>
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" />
|
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" />
|
||||||
<span class="cut-text">{{ $t('word.parameters') }} "{{ func }}"</span>
|
<span class="cut-text">{{ t('word.parameters') }} "{{ func }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #body>
|
<template #body>
|
||||||
@ -20,16 +20,16 @@
|
|||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<button class="btn btn-dark btn-sm d-flex" @click="addParameter">
|
<button class="btn btn-dark btn-sm d-flex" @click="addParameter">
|
||||||
<i class="mdi mdi-24px mdi-plus mr-1" />
|
<i class="mdi mdi-24px mdi-plus mr-1" />
|
||||||
<span>{{ $t('word.add') }}</span>
|
<span>{{ t('word.add') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-dark btn-sm d-flex ml-2 mr-0"
|
class="btn btn-dark btn-sm d-flex ml-2 mr-0"
|
||||||
:title="$t('message.clearChanges')"
|
:title="t('message.clearChanges')"
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
@click.prevent="clearChanges"
|
@click.prevent="clearChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
||||||
<span>{{ $t('word.clear') }}</span>
|
<span>{{ t('word.clear') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -55,7 +55,7 @@
|
|||||||
<div class="tile-action">
|
<div class="tile-action">
|
||||||
<button
|
<button
|
||||||
class="btn btn-link remove-field p-0 mr-2"
|
class="btn btn-link remove-field p-0 mr-2"
|
||||||
:title="$t('word.delete')"
|
:title="t('word.delete')"
|
||||||
@click.prevent="removeParameter(param._antares_id)"
|
@click.prevent="removeParameter(param._antares_id)"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-close" />
|
<i class="mdi mdi-close" />
|
||||||
@ -74,7 +74,7 @@
|
|||||||
>
|
>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label col-3">
|
<label class="form-label col-3">
|
||||||
{{ $t('word.name') }}
|
{{ t('word.name') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<input
|
<input
|
||||||
@ -86,7 +86,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label col-3">
|
<label class="form-label col-3">
|
||||||
{{ $t('word.type') }}
|
{{ t('word.type') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
@ -102,7 +102,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-if="customizations.parametersLength" class="form-group">
|
<div v-if="customizations.parametersLength" class="form-group">
|
||||||
<label class="form-label col-3">
|
<label class="form-label col-3">
|
||||||
{{ $t('word.length') }}
|
{{ t('word.length') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<input
|
<input
|
||||||
@ -115,7 +115,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-if="customizations.functionContext" class="form-group">
|
<div v-if="customizations.functionContext" class="form-group">
|
||||||
<label class="form-label col-3">
|
<label class="form-label col-3">
|
||||||
{{ $t('word.context') }}
|
{{ t('word.context') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<label class="form-radio">
|
<label class="form-radio">
|
||||||
@ -150,11 +150,11 @@
|
|||||||
<i class="mdi mdi-dots-horizontal mdi-48px" />
|
<i class="mdi mdi-dots-horizontal mdi-48px" />
|
||||||
</div>
|
</div>
|
||||||
<p class="empty-title h5">
|
<p class="empty-title h5">
|
||||||
{{ $t('message.thereAreNoParameters') }}
|
{{ t('message.thereAreNoParameters') }}
|
||||||
</p>
|
</p>
|
||||||
<div class="empty-action">
|
<div class="empty-action">
|
||||||
<button class="btn btn-primary" @click="addParameter">
|
<button class="btn btn-primary" @click="addParameter">
|
||||||
{{ $t('message.createNewParameter') }}
|
{{ t('message.createNewParameter') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -164,113 +164,118 @@
|
|||||||
</ConfirmModal>
|
</ConfirmModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
|
import { computed, onMounted, onUnmounted, Ref, ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { uidGen } from 'common/libs/uidGen';
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
import ConfirmModal from '@/components/BaseConfirmModal.vue';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
|
||||||
export default {
|
const { t } = useI18n();
|
||||||
name: 'WorkspaceTabPropsFunctionParamsModal',
|
|
||||||
components: {
|
const props = defineProps({
|
||||||
ConfirmModal,
|
|
||||||
BaseSelect
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
localParameters: {
|
localParameters: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => []
|
||||||
},
|
},
|
||||||
func: String,
|
func: String,
|
||||||
workspace: Object
|
workspace: Object
|
||||||
},
|
});
|
||||||
emits: ['hide', 'parameters-update'],
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
parametersProxy: [],
|
|
||||||
isOptionsChanging: false,
|
|
||||||
selectedParam: '',
|
|
||||||
modalInnerHeight: 400,
|
|
||||||
i: 1
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
selectedParamObj () {
|
|
||||||
return this.parametersProxy.find(param => param._antares_id === this.selectedParam);
|
|
||||||
},
|
|
||||||
isChanged () {
|
|
||||||
return JSON.stringify(this.localParameters) !== JSON.stringify(this.parametersProxy);
|
|
||||||
},
|
|
||||||
customizations () {
|
|
||||||
return this.workspace.customizations;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
this.parametersProxy = JSON.parse(JSON.stringify(this.localParameters));
|
|
||||||
this.i = this.parametersProxy.length + 1;
|
|
||||||
|
|
||||||
if (this.parametersProxy.length)
|
const emit = defineEmits(['hide', 'parameters-update']);
|
||||||
this.resetSelectedID();
|
|
||||||
|
|
||||||
this.getModalInnerHeight();
|
const parametersPanel: Ref<HTMLDivElement> = ref(null);
|
||||||
window.addEventListener('resize', this.getModalInnerHeight);
|
const parametersProxy = ref([]);
|
||||||
},
|
const selectedParam = ref('');
|
||||||
unmounted () {
|
const modalInnerHeight = ref(400);
|
||||||
window.removeEventListener('resize', this.getModalInnerHeight);
|
const i = ref(1);
|
||||||
},
|
|
||||||
methods: {
|
const selectedParamObj = computed(() => {
|
||||||
typeClass (type) {
|
return parametersProxy.value.find(param => param._antares_id === selectedParam.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
const isChanged = computed(() => {
|
||||||
|
return JSON.stringify(props.localParameters) !== JSON.stringify(parametersProxy.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
const customizations = computed(() => {
|
||||||
|
return props.workspace.customizations;
|
||||||
|
});
|
||||||
|
|
||||||
|
const typeClass = (type: string) => {
|
||||||
if (type)
|
if (type)
|
||||||
return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`;
|
return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`;
|
||||||
return '';
|
return '';
|
||||||
},
|
};
|
||||||
confirmParametersChange () {
|
|
||||||
this.$emit('parameters-update', this.parametersProxy);
|
const confirmParametersChange = () => {
|
||||||
},
|
emit('parameters-update', parametersProxy.value);
|
||||||
selectParameter (event, uid) {
|
};
|
||||||
if (this.selectedParam !== uid && !event.target.classList.contains('remove-field'))
|
|
||||||
this.selectedParam = uid;
|
const selectParameter = (event: MouseEvent, uid: string) => {
|
||||||
},
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
getModalInnerHeight () {
|
if (selectedParam.value !== uid && !(event.target as any).classList.contains('remove-field'))
|
||||||
|
selectedParam.value = uid;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getModalInnerHeight = () => {
|
||||||
const modalBody = document.querySelector('.modal-body');
|
const modalBody = document.querySelector('.modal-body');
|
||||||
if (modalBody)
|
if (modalBody)
|
||||||
this.modalInnerHeight = modalBody.clientHeight - (parseFloat(getComputedStyle(modalBody).paddingTop) + parseFloat(getComputedStyle(modalBody).paddingBottom));
|
modalInnerHeight.value = modalBody.clientHeight - (parseFloat(getComputedStyle(modalBody).paddingTop) + parseFloat(getComputedStyle(modalBody).paddingBottom));
|
||||||
},
|
};
|
||||||
addParameter () {
|
|
||||||
|
const addParameter = () => {
|
||||||
const newUid = uidGen();
|
const newUid = uidGen();
|
||||||
this.parametersProxy = [...this.parametersProxy, {
|
parametersProxy.value = [...parametersProxy.value, {
|
||||||
_antares_id: newUid,
|
_antares_id: newUid,
|
||||||
name: `param${this.i++}`,
|
name: `param${i.value++}`,
|
||||||
type: this.workspace.dataTypes[0].types[0].name,
|
type: props.workspace.dataTypes[0].types[0].name,
|
||||||
context: 'IN',
|
context: 'IN',
|
||||||
length: ''
|
length: ''
|
||||||
}];
|
}];
|
||||||
|
|
||||||
if (this.parametersProxy.length === 1)
|
if (parametersProxy.value.length === 1)
|
||||||
this.resetSelectedID();
|
resetSelectedID();
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.$refs.parametersPanel.scrollTop = this.$refs.parametersPanel.scrollHeight + 60;
|
parametersPanel.value.scrollTop = parametersPanel.value.scrollHeight + 60;
|
||||||
this.selectedParam = newUid;
|
selectedParam.value = newUid;
|
||||||
}, 20);
|
}, 20);
|
||||||
},
|
|
||||||
removeParameter (uid) {
|
|
||||||
this.parametersProxy = this.parametersProxy.filter(param => param._antares_id !== uid);
|
|
||||||
|
|
||||||
if (this.parametersProxy.length && this.selectedParam === uid)
|
|
||||||
this.resetSelectedID();
|
|
||||||
},
|
|
||||||
clearChanges () {
|
|
||||||
this.parametersProxy = JSON.parse(JSON.stringify(this.localParameters));
|
|
||||||
this.i = this.parametersProxy.length + 1;
|
|
||||||
|
|
||||||
if (!this.parametersProxy.some(param => param.name === this.selectedParam))
|
|
||||||
this.resetSelectedID();
|
|
||||||
},
|
|
||||||
resetSelectedID () {
|
|
||||||
this.selectedParam = this.parametersProxy.length ? this.parametersProxy[0]._antares_id : '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const removeParameter = (uid: string) => {
|
||||||
|
parametersProxy.value = parametersProxy.value.filter(param => param._antares_id !== uid);
|
||||||
|
|
||||||
|
if (parametersProxy.value.length && selectedParam.value === uid)
|
||||||
|
resetSelectedID();
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearChanges = () => {
|
||||||
|
parametersProxy.value = JSON.parse(JSON.stringify(props.localParameters));
|
||||||
|
i.value = parametersProxy.value.length + 1;
|
||||||
|
|
||||||
|
if (!parametersProxy.value.some(param => param.name === selectedParam.value))
|
||||||
|
resetSelectedID();
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetSelectedID = () => {
|
||||||
|
selectedParam.value = parametersProxy.value.length ? parametersProxy.value[0]._antares_id : '';
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
parametersProxy.value = JSON.parse(JSON.stringify(props.localParameters));
|
||||||
|
i.value = parametersProxy.value.length + 1;
|
||||||
|
|
||||||
|
if (parametersProxy.value.length)
|
||||||
|
resetSelectedID();
|
||||||
|
|
||||||
|
getModalInnerHeight();
|
||||||
|
window.addEventListener('resize', getModalInnerHeight);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('resize', getModalInnerHeight);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -11,16 +11,16 @@
|
|||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
<span>{{ $t('word.save') }}</span>
|
<span>{{ t('word.save') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
class="btn btn-link btn-sm mr-0"
|
class="btn btn-link btn-sm mr-0"
|
||||||
:title="$t('message.clearChanges')"
|
:title="t('message.clearChanges')"
|
||||||
@click="clearChanges"
|
@click="clearChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
||||||
<span>{{ $t('word.clear') }}</span>
|
<span>{{ t('word.clear') }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="divider-vert py-3" />
|
<div class="divider-vert py-3" />
|
||||||
@ -31,15 +31,15 @@
|
|||||||
@click="runRoutineCheck"
|
@click="runRoutineCheck"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-play mr-1" />
|
<i class="mdi mdi-24px mdi-play mr-1" />
|
||||||
<span>{{ $t('word.run') }}</span>
|
<span>{{ t('word.run') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-dark btn-sm" @click="showParamsModal">
|
<button class="btn btn-dark btn-sm" @click="showParamsModal">
|
||||||
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" />
|
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" />
|
||||||
<span>{{ $t('word.parameters') }}</span>
|
<span>{{ t('word.parameters') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="workspace-query-info">
|
<div class="workspace-query-info">
|
||||||
<div class="d-flex" :title="$t('word.schema')">
|
<div class="d-flex" :title="t('word.schema')">
|
||||||
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
|
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -50,7 +50,7 @@
|
|||||||
<div class="column col-auto">
|
<div class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('word.name') }}
|
{{ t('word.name') }}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
ref="firstInput"
|
ref="firstInput"
|
||||||
@ -64,7 +64,7 @@
|
|||||||
<div v-if="customizations.languages" class="column col-auto">
|
<div v-if="customizations.languages" class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('word.language') }}
|
{{ t('word.language') }}
|
||||||
</label>
|
</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localRoutine.language"
|
v-model="localRoutine.language"
|
||||||
@ -76,13 +76,13 @@
|
|||||||
<div v-if="customizations.definer" class="column col-auto">
|
<div v-if="customizations.definer" class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('word.definer') }}
|
{{ t('word.definer') }}
|
||||||
</label>
|
</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localRoutine.definer"
|
v-model="localRoutine.definer"
|
||||||
:options="[{value: '', name:$t('message.currentUser')}, ...workspace.users]"
|
:options="[{value: '', name: t('message.currentUser')}, ...workspace.users]"
|
||||||
:option-label="(user) => user.value === '' ? user.name : `${user.name}@${user.host}`"
|
:option-label="(user: any) => user.value === '' ? user.name : `${user.name}@${user.host}`"
|
||||||
:option-track-by="(user) => user.value === '' ? '' : `\`${user.name}\`@\`${user.host}\``"
|
:option-track-by="(user: any) => user.value === '' ? '' : `\`${user.name}\`@\`${user.host}\``"
|
||||||
class="form-select"
|
class="form-select"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -90,7 +90,7 @@
|
|||||||
<div v-if="customizations.comment" class="column">
|
<div v-if="customizations.comment" class="column">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('word.comment') }}
|
{{ t('word.comment') }}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
v-model="localRoutine.comment"
|
v-model="localRoutine.comment"
|
||||||
@ -102,7 +102,7 @@
|
|||||||
<div class="column col-auto">
|
<div class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('message.sqlSecurity') }}
|
{{ t('message.sqlSecurity') }}
|
||||||
</label>
|
</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localRoutine.security"
|
v-model="localRoutine.security"
|
||||||
@ -114,7 +114,7 @@
|
|||||||
<div v-if="customizations.procedureDataAccess" class="column col-auto">
|
<div v-if="customizations.procedureDataAccess" class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('message.dataAccess') }}
|
{{ t('message.dataAccess') }}
|
||||||
</label>
|
</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localRoutine.dataAccess"
|
v-model="localRoutine.dataAccess"
|
||||||
@ -127,7 +127,7 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label d-invisible">.</label>
|
<label class="form-label d-invisible">.</label>
|
||||||
<label class="form-checkbox form-inline">
|
<label class="form-checkbox form-inline">
|
||||||
<input v-model="localRoutine.deterministic" type="checkbox"><i class="form-icon" /> {{ $t('word.deterministic') }}
|
<input v-model="localRoutine.deterministic" type="checkbox"><i class="form-icon" /> {{ t('word.deterministic') }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -135,7 +135,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
||||||
<BaseLoader v-if="isLoading" />
|
<BaseLoader v-if="isLoading" />
|
||||||
<label class="form-label ml-2">{{ $t('message.routineBody') }}</label>
|
<label class="form-label ml-2">{{ t('message.routineBody') }}</label>
|
||||||
<QueryEditor
|
<QueryEditor
|
||||||
v-show="isSelected"
|
v-show="isSelected"
|
||||||
ref="queryEditor"
|
ref="queryEditor"
|
||||||
@ -163,40 +163,34 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import { storeToRefs } from 'pinia';
|
import { Component, computed, onUnmounted, onBeforeUnmount, onMounted, Ref, ref, watch } from 'vue';
|
||||||
|
import { AlterRoutineParams, FunctionParam, RoutineInfos } from 'common/interfaces/antares';
|
||||||
|
import { Ace } from 'ace-builds';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
import { useNotificationsStore } from '@/stores/notifications';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
import { uidGen } from 'common/libs/uidGen';
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
import QueryEditor from '@/components/QueryEditor';
|
|
||||||
import BaseLoader from '@/components/BaseLoader';
|
|
||||||
import WorkspaceTabPropsRoutineParamsModal from '@/components/WorkspaceTabPropsRoutineParamsModal';
|
|
||||||
import ModalAskParameters from '@/components/ModalAskParameters';
|
|
||||||
import Routines from '@/ipc-api/Routines';
|
import Routines from '@/ipc-api/Routines';
|
||||||
|
import QueryEditor from '@/components/QueryEditor.vue';
|
||||||
|
import BaseLoader from '@/components/BaseLoader.vue';
|
||||||
|
import WorkspaceTabPropsRoutineParamsModal from '@/components/WorkspaceTabPropsRoutineParamsModal.vue';
|
||||||
|
import ModalAskParameters from '@/components/ModalAskParameters.vue';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
|
||||||
export default {
|
const { t } = useI18n();
|
||||||
name: 'WorkspaceTabPropsRoutine',
|
|
||||||
components: {
|
const props = defineProps({
|
||||||
QueryEditor,
|
|
||||||
BaseLoader,
|
|
||||||
WorkspaceTabPropsRoutineParamsModal,
|
|
||||||
ModalAskParameters,
|
|
||||||
BaseSelect
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
tabUid: String,
|
tabUid: String,
|
||||||
connection: Object,
|
connection: Object,
|
||||||
routine: String,
|
routine: String,
|
||||||
isSelected: Boolean,
|
isSelected: Boolean,
|
||||||
schema: String
|
schema: String
|
||||||
},
|
});
|
||||||
setup () {
|
|
||||||
const { addNotification } = useNotificationsStore();
|
const { addNotification } = useNotificationsStore();
|
||||||
const workspacesStore = useWorkspacesStore();
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
|
||||||
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getWorkspace,
|
getWorkspace,
|
||||||
refreshStructure,
|
refreshStructure,
|
||||||
@ -206,243 +200,235 @@ export default {
|
|||||||
setUnsavedChanges
|
setUnsavedChanges
|
||||||
} = workspacesStore;
|
} = workspacesStore;
|
||||||
|
|
||||||
return {
|
const queryEditor: Ref<Component & {editor: Ace.Editor; $el: HTMLElement}> = ref(null);
|
||||||
addNotification,
|
const firstInput: Ref<HTMLInputElement> = ref(null);
|
||||||
selectedWorkspace,
|
const isLoading = ref(false);
|
||||||
getWorkspace,
|
const isSaving = ref(false);
|
||||||
refreshStructure,
|
const isParamsModal = ref(false);
|
||||||
renameTabs,
|
const isAskingParameters = ref(false);
|
||||||
newTab,
|
const originalRoutine: Ref<RoutineInfos> = ref(null);
|
||||||
changeBreadcrumbs,
|
const localRoutine: Ref<RoutineInfos> = ref(null);
|
||||||
setUnsavedChanges
|
const lastRoutine = ref(null);
|
||||||
};
|
const sqlProxy = ref('');
|
||||||
},
|
const editorHeight = ref(300);
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
isLoading: false,
|
|
||||||
isSaving: false,
|
|
||||||
isParamsModal: false,
|
|
||||||
isAskingParameters: false,
|
|
||||||
originalRoutine: null,
|
|
||||||
localRoutine: { sql: '' },
|
|
||||||
lastRoutine: null,
|
|
||||||
sqlProxy: '',
|
|
||||||
editorHeight: 300
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
workspace () {
|
|
||||||
return this.getWorkspace(this.connection.uid);
|
|
||||||
},
|
|
||||||
customizations () {
|
|
||||||
return this.workspace.customizations;
|
|
||||||
},
|
|
||||||
isChanged () {
|
|
||||||
return JSON.stringify(this.originalRoutine) !== JSON.stringify(this.localRoutine);
|
|
||||||
},
|
|
||||||
isDefinerInUsers () {
|
|
||||||
return this.originalRoutine ? this.workspace.users.some(user => this.originalRoutine.definer === `\`${user.name}\`@\`${user.host}\``) : true;
|
|
||||||
},
|
|
||||||
isTableNameValid () {
|
|
||||||
return this.localRoutine.name !== '';
|
|
||||||
},
|
|
||||||
schemaTables () {
|
|
||||||
const schemaTables = this.workspace.structure
|
|
||||||
.filter(schema => schema.name === this.schema)
|
|
||||||
.map(schema => schema.tables);
|
|
||||||
|
|
||||||
return schemaTables.length ? schemaTables[0].filter(table => table.type === 'table') : [];
|
const workspace = computed(() => {
|
||||||
}
|
return getWorkspace(props.connection.uid);
|
||||||
},
|
});
|
||||||
watch: {
|
|
||||||
async schema () {
|
|
||||||
if (this.isSelected) {
|
|
||||||
await this.getRoutineData();
|
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localRoutine.sql);
|
|
||||||
this.lastRoutine = this.routine;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async routine () {
|
|
||||||
if (this.isSelected) {
|
|
||||||
await this.getRoutineData();
|
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localRoutine.sql);
|
|
||||||
this.lastRoutine = this.routine;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async isSelected (val) {
|
|
||||||
if (val) {
|
|
||||||
this.changeBreadcrumbs({ schema: this.schema, routine: this.routine });
|
|
||||||
|
|
||||||
setTimeout(() => {
|
const customizations = computed(() => {
|
||||||
this.resizeQueryEditor();
|
return workspace.value.customizations;
|
||||||
}, 200);
|
});
|
||||||
|
|
||||||
if (this.lastRoutine !== this.routine)
|
const isChanged = computed(() => {
|
||||||
this.getRoutineData();
|
return JSON.stringify(originalRoutine.value) !== JSON.stringify(localRoutine.value);
|
||||||
}
|
});
|
||||||
},
|
|
||||||
isChanged (val) {
|
|
||||||
this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async created () {
|
|
||||||
await this.getRoutineData();
|
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localRoutine.sql);
|
|
||||||
window.addEventListener('keydown', this.onKey);
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
window.addEventListener('resize', this.resizeQueryEditor);
|
|
||||||
},
|
|
||||||
unmounted () {
|
|
||||||
window.removeEventListener('resize', this.resizeQueryEditor);
|
|
||||||
},
|
|
||||||
beforeUnmount () {
|
|
||||||
window.removeEventListener('keydown', this.onKey);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async getRoutineData () {
|
|
||||||
if (!this.routine) return;
|
|
||||||
|
|
||||||
this.localRoutine = { sql: '' };
|
const isTableNameValid = computed(() => {
|
||||||
this.isLoading = true;
|
return localRoutine.value.name !== '';
|
||||||
this.lastRoutine = this.routine;
|
});
|
||||||
|
|
||||||
|
const getRoutineData = async () => {
|
||||||
|
if (!props.routine) return;
|
||||||
|
|
||||||
|
localRoutine.value = { name: '', sql: '', definer: null };
|
||||||
|
isLoading.value = true;
|
||||||
|
lastRoutine.value = props.routine;
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: props.connection.uid,
|
||||||
schema: this.schema,
|
schema: props.schema,
|
||||||
routine: this.routine
|
routine: props.routine
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { status, response } = await Routines.getRoutineInformations(params);
|
const { status, response } = await Routines.getRoutineInformations(params);
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
this.originalRoutine = response;
|
originalRoutine.value = response;
|
||||||
|
|
||||||
this.originalRoutine.parameters = [...this.originalRoutine.parameters.map(param => {
|
originalRoutine.value.parameters = [...originalRoutine.value.parameters.map(param => {
|
||||||
param._antares_id = uidGen();
|
param._antares_id = uidGen();
|
||||||
return param;
|
return param;
|
||||||
})];
|
})];
|
||||||
|
|
||||||
this.localRoutine = JSON.parse(JSON.stringify(this.originalRoutine));
|
localRoutine.value = JSON.parse(JSON.stringify(originalRoutine.value));
|
||||||
this.sqlProxy = this.localRoutine.sql;
|
sqlProxy.value = localRoutine.value.sql;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
addNotification({ status: 'error', message: response });
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.resizeQueryEditor();
|
resizeQueryEditor();
|
||||||
this.isLoading = false;
|
isLoading.value = false;
|
||||||
},
|
};
|
||||||
async saveChanges () {
|
|
||||||
if (this.isSaving) return;
|
const saveChanges = async () => {
|
||||||
this.isSaving = true;
|
if (isSaving.value) return;
|
||||||
|
isSaving.value = true;
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: props.connection.uid as string,
|
||||||
routine: {
|
routine: {
|
||||||
...this.localRoutine,
|
...localRoutine.value,
|
||||||
schema: this.schema,
|
schema: props.schema,
|
||||||
oldName: this.originalRoutine.name
|
oldName: originalRoutine.value.name
|
||||||
}
|
} as AlterRoutineParams
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { status, response } = await Routines.alterRoutine(params);
|
const { status, response } = await Routines.alterRoutine(params);
|
||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
const oldName = this.originalRoutine.name;
|
const oldName = originalRoutine.value.name;
|
||||||
|
|
||||||
await this.refreshStructure(this.connection.uid);
|
await refreshStructure(props.connection.uid);
|
||||||
|
|
||||||
if (oldName !== this.localRoutine.name) {
|
if (oldName !== localRoutine.value.name) {
|
||||||
this.renameTabs({
|
renameTabs({
|
||||||
uid: this.connection.uid,
|
uid: props.connection.uid,
|
||||||
schema: this.schema,
|
schema: props.schema,
|
||||||
elementName: oldName,
|
elementName: oldName,
|
||||||
elementNewName: this.localRoutine.name,
|
elementNewName: localRoutine.value.name,
|
||||||
elementType: 'procedure'
|
elementType: 'procedure'
|
||||||
});
|
});
|
||||||
|
|
||||||
this.changeBreadcrumbs({ schema: this.schema, procedure: this.localRoutine.name });
|
changeBreadcrumbs({ schema: props.schema, routine: localRoutine.value.name });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.getRoutineData();
|
getRoutineData();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
addNotification({ status: 'error', message: response });
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isSaving = false;
|
isSaving.value = false;
|
||||||
},
|
};
|
||||||
clearChanges () {
|
|
||||||
this.localRoutine = JSON.parse(JSON.stringify(this.originalRoutine));
|
const clearChanges = () => {
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localRoutine.sql);
|
localRoutine.value = JSON.parse(JSON.stringify(originalRoutine.value));
|
||||||
},
|
queryEditor.value.editor.session.setValue(localRoutine.value.sql);
|
||||||
resizeQueryEditor () {
|
};
|
||||||
if (this.$refs.queryEditor) {
|
|
||||||
|
const resizeQueryEditor = () => {
|
||||||
|
if (queryEditor.value) {
|
||||||
const footer = document.getElementById('footer');
|
const footer = document.getElementById('footer');
|
||||||
const size = window.innerHeight - this.$refs.queryEditor.$el.getBoundingClientRect().top - footer.offsetHeight;
|
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - footer.offsetHeight;
|
||||||
this.editorHeight = size;
|
editorHeight.value = size;
|
||||||
this.$refs.queryEditor.editor.resize();
|
queryEditor.value.editor.resize();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
optionsUpdate (options) {
|
|
||||||
this.localRoutine = options;
|
const parametersUpdate = (parameters: FunctionParam[]) => {
|
||||||
},
|
localRoutine.value = { ...localRoutine.value, parameters };
|
||||||
parametersUpdate (parameters) {
|
};
|
||||||
this.localRoutine = { ...this.localRoutine, parameters };
|
|
||||||
},
|
const runRoutineCheck = () => {
|
||||||
runRoutineCheck () {
|
if (localRoutine.value.parameters.length)
|
||||||
if (this.localRoutine.parameters.length)
|
showAskParamsModal();
|
||||||
this.showAskParamsModal();
|
|
||||||
else
|
else
|
||||||
this.runRoutine();
|
runRoutine();
|
||||||
},
|
};
|
||||||
runRoutine (params) {
|
|
||||||
|
const runRoutine = (params?: string[]) => {
|
||||||
if (!params) params = [];
|
if (!params) params = [];
|
||||||
|
|
||||||
let sql;
|
let sql;
|
||||||
switch (this.connection.client) { // TODO: move in a better place
|
switch (props.connection.client) { // TODO: move in a better place
|
||||||
case 'maria':
|
case 'maria':
|
||||||
case 'mysql':
|
case 'mysql':
|
||||||
case 'pg':
|
case 'pg':
|
||||||
sql = `CALL ${this.originalRoutine.name}(${params.join(',')})`;
|
sql = `CALL ${originalRoutine.value.name}(${params.join(',')})`;
|
||||||
break;
|
break;
|
||||||
case 'mssql':
|
case 'mssql':
|
||||||
sql = `EXEC ${this.originalRoutine.name} ${params.join(',')}`;
|
sql = `EXEC ${originalRoutine.value.name} ${params.join(',')}`;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
sql = `CALL \`${this.originalRoutine.name}\`(${params.join(',')})`;
|
sql = `CALL \`${originalRoutine.value.name}\`(${params.join(',')})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.newTab({ uid: this.connection.uid, content: sql, type: 'query', autorun: true });
|
newTab({ uid: props.connection.uid, content: sql, type: 'query', autorun: true });
|
||||||
},
|
};
|
||||||
showParamsModal () {
|
|
||||||
this.isParamsModal = true;
|
const showParamsModal = () => {
|
||||||
},
|
isParamsModal.value = true;
|
||||||
hideParamsModal () {
|
};
|
||||||
this.isParamsModal = false;
|
|
||||||
},
|
const hideParamsModal = () => {
|
||||||
showAskParamsModal () {
|
isParamsModal.value = false;
|
||||||
this.isAskingParameters = true;
|
};
|
||||||
},
|
|
||||||
hideAskParamsModal () {
|
const showAskParamsModal = () => {
|
||||||
this.isAskingParameters = false;
|
isAskingParameters.value = true;
|
||||||
},
|
};
|
||||||
onKey (e) {
|
|
||||||
if (this.isSelected) {
|
const hideAskParamsModal = () => {
|
||||||
|
isAskingParameters.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onKey = (e: KeyboardEvent) => {
|
||||||
|
if (props.isSelected) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (e.ctrlKey && e.keyCode === 83) { // CTRL + S
|
if (e.ctrlKey && e.key === 's') { // CTRL + S
|
||||||
if (this.isChanged)
|
if (isChanged.value)
|
||||||
this.saveChanges();
|
saveChanges();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(() => props.schema, async () => {
|
||||||
|
if (props.isSelected) {
|
||||||
|
await getRoutineData();
|
||||||
|
queryEditor.value.editor.session.setValue(localRoutine.value.sql);
|
||||||
|
lastRoutine.value = props.routine;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => props.routine, async () => {
|
||||||
|
if (props.isSelected) {
|
||||||
|
await getRoutineData();
|
||||||
|
queryEditor.value.editor.session.setValue(localRoutine.value.sql);
|
||||||
|
lastRoutine.value = props.routine;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => props.isSelected, async (val) => {
|
||||||
|
if (val) {
|
||||||
|
changeBreadcrumbs({ schema: props.schema, routine: props.routine });
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
resizeQueryEditor();
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
if (lastRoutine.value !== props.routine)
|
||||||
|
getRoutineData();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(isChanged, (val) => {
|
||||||
|
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: val });
|
||||||
|
});
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
await getRoutineData();
|
||||||
|
queryEditor.value.editor.session.setValue(localRoutine.value.sql);
|
||||||
|
window.addEventListener('keydown', onKey);
|
||||||
|
})();
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('resize', resizeQueryEditor);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('resize', resizeQueryEditor);
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.removeEventListener('keydown', onKey);
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
:confirm-text="$t('word.confirm')"
|
:confirm-text="t('word.confirm')"
|
||||||
size="medium"
|
size="medium"
|
||||||
class="options-modal"
|
class="options-modal"
|
||||||
@confirm="confirmParametersChange"
|
@confirm="confirmParametersChange"
|
||||||
@ -9,7 +9,7 @@
|
|||||||
<template #header>
|
<template #header>
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" />
|
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" />
|
||||||
<span class="cut-text">{{ $t('word.parameters') }} "{{ routine }}"</span>
|
<span class="cut-text">{{ t('word.parameters') }} "{{ routine }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #body>
|
<template #body>
|
||||||
@ -20,16 +20,16 @@
|
|||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<button class="btn btn-dark btn-sm d-flex" @click="addParameter">
|
<button class="btn btn-dark btn-sm d-flex" @click="addParameter">
|
||||||
<i class="mdi mdi-24px mdi-plus mr-1" />
|
<i class="mdi mdi-24px mdi-plus mr-1" />
|
||||||
<span>{{ $t('word.add') }}</span>
|
<span>{{ t('word.add') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-dark btn-sm d-flex ml-2 mr-0"
|
class="btn btn-dark btn-sm d-flex ml-2 mr-0"
|
||||||
:title="$t('message.clearChanges')"
|
:title="t('message.clearChanges')"
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
@click.prevent="clearChanges"
|
@click.prevent="clearChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
||||||
<span>{{ $t('word.clear') }}</span>
|
<span>{{ t('word.clear') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -55,7 +55,7 @@
|
|||||||
<div class="tile-action">
|
<div class="tile-action">
|
||||||
<button
|
<button
|
||||||
class="btn btn-link remove-field p-0 mr-2"
|
class="btn btn-link remove-field p-0 mr-2"
|
||||||
:title="$t('word.delete')"
|
:title="t('word.delete')"
|
||||||
@click.prevent="removeParameter(param._antares_id)"
|
@click.prevent="removeParameter(param._antares_id)"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-close" />
|
<i class="mdi mdi-close" />
|
||||||
@ -74,7 +74,7 @@
|
|||||||
>
|
>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label col-3">
|
<label class="form-label col-3">
|
||||||
{{ $t('word.name') }}
|
{{ t('word.name') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<input
|
<input
|
||||||
@ -86,7 +86,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label col-3">
|
<label class="form-label col-3">
|
||||||
{{ $t('word.type') }}
|
{{ t('word.type') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
@ -102,7 +102,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-if="customizations.parametersLength" class="form-group">
|
<div v-if="customizations.parametersLength" class="form-group">
|
||||||
<label class="form-label col-3">
|
<label class="form-label col-3">
|
||||||
{{ $t('word.length') }}
|
{{ t('word.length') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<input
|
<input
|
||||||
@ -115,7 +115,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-if="customizations.procedureContext" class="form-group">
|
<div v-if="customizations.procedureContext" class="form-group">
|
||||||
<label class="form-label col-3">
|
<label class="form-label col-3">
|
||||||
{{ $t('word.context') }}
|
{{ t('word.context') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<label class="form-radio">
|
<label class="form-radio">
|
||||||
@ -150,11 +150,11 @@
|
|||||||
<i class="mdi mdi-dots-horizontal mdi-48px" />
|
<i class="mdi mdi-dots-horizontal mdi-48px" />
|
||||||
</div>
|
</div>
|
||||||
<p class="empty-title h5">
|
<p class="empty-title h5">
|
||||||
{{ $t('message.thereAreNoParameters') }}
|
{{ t('message.thereAreNoParameters') }}
|
||||||
</p>
|
</p>
|
||||||
<div class="empty-action">
|
<div class="empty-action">
|
||||||
<button class="btn btn-primary" @click="addParameter">
|
<button class="btn btn-primary" @click="addParameter">
|
||||||
{{ $t('message.createNewParameter') }}
|
{{ t('message.createNewParameter') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -164,113 +164,118 @@
|
|||||||
</ConfirmModal>
|
</ConfirmModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
|
import { computed, onMounted, onUnmounted, Ref, ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { uidGen } from 'common/libs/uidGen';
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
import ConfirmModal from '@/components/BaseConfirmModal.vue';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
|
||||||
export default {
|
const { t } = useI18n();
|
||||||
name: 'WorkspaceTabPropsRoutineParamsModal',
|
|
||||||
components: {
|
const props = defineProps({
|
||||||
ConfirmModal,
|
|
||||||
BaseSelect
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
localParameters: {
|
localParameters: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => []
|
||||||
},
|
},
|
||||||
routine: String,
|
routine: String,
|
||||||
workspace: Object
|
workspace: Object
|
||||||
},
|
});
|
||||||
emits: ['parameters-update', 'hide'],
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
parametersProxy: [],
|
|
||||||
isOptionsChanging: false,
|
|
||||||
selectedParam: '',
|
|
||||||
modalInnerHeight: 400,
|
|
||||||
i: 1
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
selectedParamObj () {
|
|
||||||
return this.parametersProxy.find(param => param._antares_id === this.selectedParam);
|
|
||||||
},
|
|
||||||
isChanged () {
|
|
||||||
return JSON.stringify(this.localParameters) !== JSON.stringify(this.parametersProxy);
|
|
||||||
},
|
|
||||||
customizations () {
|
|
||||||
return this.workspace.customizations;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
this.parametersProxy = JSON.parse(JSON.stringify(this.localParameters));
|
|
||||||
this.i = this.parametersProxy.length + 1;
|
|
||||||
|
|
||||||
if (this.parametersProxy.length)
|
const emit = defineEmits(['hide', 'parameters-update']);
|
||||||
this.resetSelectedID();
|
|
||||||
|
|
||||||
this.getModalInnerHeight();
|
const parametersPanel: Ref<HTMLDivElement> = ref(null);
|
||||||
window.addEventListener('resize', this.getModalInnerHeight);
|
const parametersProxy = ref([]);
|
||||||
},
|
const selectedParam = ref('');
|
||||||
unmounted () {
|
const modalInnerHeight = ref(400);
|
||||||
window.removeEventListener('resize', this.getModalInnerHeight);
|
const i = ref(1);
|
||||||
},
|
|
||||||
methods: {
|
const selectedParamObj = computed(() => {
|
||||||
typeClass (type) {
|
return parametersProxy.value.find(param => param._antares_id === selectedParam.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
const isChanged = computed(() => {
|
||||||
|
return JSON.stringify(props.localParameters) !== JSON.stringify(parametersProxy.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
const customizations = computed(() => {
|
||||||
|
return props.workspace.customizations;
|
||||||
|
});
|
||||||
|
|
||||||
|
const typeClass = (type: string) => {
|
||||||
if (type)
|
if (type)
|
||||||
return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`;
|
return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`;
|
||||||
return '';
|
return '';
|
||||||
},
|
};
|
||||||
confirmParametersChange () {
|
|
||||||
this.$emit('parameters-update', this.parametersProxy);
|
const confirmParametersChange = () => {
|
||||||
},
|
emit('parameters-update', parametersProxy.value);
|
||||||
selectParameter (event, uid) {
|
};
|
||||||
if (this.selectedParam !== uid && !event.target.classList.contains('remove-field'))
|
|
||||||
this.selectedParam = uid;
|
const selectParameter = (event: MouseEvent, uid: string) => {
|
||||||
},
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
getModalInnerHeight () {
|
if (selectedParam.value !== uid && !(event.target as any).classList.contains('remove-field'))
|
||||||
|
selectedParam.value = uid;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getModalInnerHeight = () => {
|
||||||
const modalBody = document.querySelector('.modal-body');
|
const modalBody = document.querySelector('.modal-body');
|
||||||
if (modalBody)
|
if (modalBody)
|
||||||
this.modalInnerHeight = modalBody.clientHeight - (parseFloat(getComputedStyle(modalBody).paddingTop) + parseFloat(getComputedStyle(modalBody).paddingBottom));
|
modalInnerHeight.value = modalBody.clientHeight - (parseFloat(getComputedStyle(modalBody).paddingTop) + parseFloat(getComputedStyle(modalBody).paddingBottom));
|
||||||
},
|
};
|
||||||
addParameter () {
|
|
||||||
|
const addParameter = () => {
|
||||||
const newUid = uidGen();
|
const newUid = uidGen();
|
||||||
this.parametersProxy = [...this.parametersProxy, {
|
parametersProxy.value = [...parametersProxy.value, {
|
||||||
_antares_id: newUid,
|
_antares_id: newUid,
|
||||||
name: `param${this.i++}`,
|
name: `param${i.value++}`,
|
||||||
type: this.workspace.dataTypes[0].types[0].name,
|
type: props.workspace.dataTypes[0].types[0].name,
|
||||||
context: 'IN',
|
context: 'IN',
|
||||||
length: ''
|
length: ''
|
||||||
}];
|
}];
|
||||||
|
|
||||||
if (this.parametersProxy.length === 1)
|
if (parametersProxy.value.length === 1)
|
||||||
this.resetSelectedID();
|
resetSelectedID();
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.$refs.parametersPanel.scrollTop = this.$refs.parametersPanel.scrollHeight + 60;
|
parametersPanel.value.scrollTop = parametersPanel.value.scrollHeight + 60;
|
||||||
this.selectedParam = newUid;
|
selectedParam.value = newUid;
|
||||||
}, 20);
|
}, 20);
|
||||||
},
|
|
||||||
removeParameter (uid) {
|
|
||||||
this.parametersProxy = this.parametersProxy.filter(param => param._antares_id !== uid);
|
|
||||||
|
|
||||||
if (this.parametersProxy.length && this.selectedParam === uid)
|
|
||||||
this.resetSelectedID();
|
|
||||||
},
|
|
||||||
clearChanges () {
|
|
||||||
this.parametersProxy = JSON.parse(JSON.stringify(this.localParameters));
|
|
||||||
this.i = this.parametersProxy.length + 1;
|
|
||||||
|
|
||||||
if (!this.parametersProxy.some(param => param.name === this.selectedParam))
|
|
||||||
this.resetSelectedID();
|
|
||||||
},
|
|
||||||
resetSelectedID () {
|
|
||||||
this.selectedParam = this.parametersProxy.length ? this.parametersProxy[0]._antares_id : '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const removeParameter = (uid: string) => {
|
||||||
|
parametersProxy.value = parametersProxy.value.filter(param => param._antares_id !== uid);
|
||||||
|
|
||||||
|
if (parametersProxy.value.length && selectedParam.value === uid)
|
||||||
|
resetSelectedID();
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearChanges = () => {
|
||||||
|
parametersProxy.value = JSON.parse(JSON.stringify(props.localParameters));
|
||||||
|
i.value = parametersProxy.value.length + 1;
|
||||||
|
|
||||||
|
if (!parametersProxy.value.some(param => param.name === selectedParam.value))
|
||||||
|
resetSelectedID();
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetSelectedID = () => {
|
||||||
|
selectedParam.value = parametersProxy.value.length ? parametersProxy.value[0]._antares_id : '';
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
parametersProxy.value = JSON.parse(JSON.stringify(props.localParameters));
|
||||||
|
i.value = parametersProxy.value.length + 1;
|
||||||
|
|
||||||
|
if (parametersProxy.value.length)
|
||||||
|
resetSelectedID();
|
||||||
|
|
||||||
|
getModalInnerHeight();
|
||||||
|
window.addEventListener('resize', getModalInnerHeight);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('resize', getModalInnerHeight);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -11,26 +11,26 @@
|
|||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
<span>{{ $t('word.save') }}</span>
|
<span>{{ t('word.save') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
class="btn btn-link btn-sm mr-0"
|
class="btn btn-link btn-sm mr-0"
|
||||||
:title="$t('message.clearChanges')"
|
:title="t('message.clearChanges')"
|
||||||
@click="clearChanges"
|
@click="clearChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
||||||
<span>{{ $t('word.clear') }}</span>
|
<span>{{ t('word.clear') }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="divider-vert py-3" />
|
<div class="divider-vert py-3" />
|
||||||
<button class="btn btn-dark btn-sm" @click="showTimingModal">
|
<button class="btn btn-dark btn-sm" @click="showTimingModal">
|
||||||
<i class="mdi mdi-24px mdi-timer mr-1" />
|
<i class="mdi mdi-24px mdi-timer mr-1" />
|
||||||
<span>{{ $t('word.timing') }}</span>
|
<span>{{ t('word.timing') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="workspace-query-info">
|
<div class="workspace-query-info">
|
||||||
<div class="d-flex" :title="$t('word.schema')">
|
<div class="d-flex" :title="t('word.schema')">
|
||||||
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
|
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -40,7 +40,7 @@
|
|||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column col-auto">
|
<div class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">{{ $t('word.name') }}</label>
|
<label class="form-label">{{ t('word.name') }}</label>
|
||||||
<input
|
<input
|
||||||
v-model="localScheduler.name"
|
v-model="localScheduler.name"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
@ -50,19 +50,19 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="column col-auto">
|
<div class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">{{ $t('word.definer') }}</label>
|
<label class="form-label">{{ t('word.definer') }}</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localScheduler.definer"
|
v-model="localScheduler.definer"
|
||||||
:options="users"
|
:options="users"
|
||||||
:option-label="(user) => user.value === '' ? $t('message.currentUser') : `${user.name}@${user.host}`"
|
:option-label="(user: any) => user.value === '' ? t('message.currentUser') : `${user.name}@${user.host}`"
|
||||||
:option-track-by="(user) => user.value === '' ? '' : `\`${user.name}\`@\`${user.host}\``"
|
:option-track-by="(user: any) => user.value === '' ? '' : `\`${user.name}\`@\`${user.host}\``"
|
||||||
class="form-select"
|
class="form-select"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">{{ $t('word.comment') }}</label>
|
<label class="form-label">{{ t('word.comment') }}</label>
|
||||||
<input
|
<input
|
||||||
v-model="localScheduler.comment"
|
v-model="localScheduler.comment"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
@ -72,7 +72,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label mr-2">{{ $t('word.state') }}</label>
|
<label class="form-label mr-2">{{ t('word.state') }}</label>
|
||||||
<label class="form-radio form-inline">
|
<label class="form-radio form-inline">
|
||||||
<input
|
<input
|
||||||
v-model="localScheduler.state"
|
v-model="localScheduler.state"
|
||||||
@ -103,7 +103,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
||||||
<BaseLoader v-if="isLoading" />
|
<BaseLoader v-if="isLoading" />
|
||||||
<label class="form-label ml-2">{{ $t('message.schedulerBody') }}</label>
|
<label class="form-label ml-2">{{ t('message.schedulerBody') }}</label>
|
||||||
<QueryEditor
|
<QueryEditor
|
||||||
v-show="isSelected"
|
v-show="isSelected"
|
||||||
ref="queryEditor"
|
ref="queryEditor"
|
||||||
@ -122,245 +122,231 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
<script>
|
import { AlterEventParams, EventInfos } from 'common/interfaces/antares';
|
||||||
import { storeToRefs } from 'pinia';
|
import { Component, computed, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch } from 'vue';
|
||||||
|
import { Ace } from 'ace-builds';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
import { useNotificationsStore } from '@/stores/notifications';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
import BaseLoader from '@/components/BaseLoader';
|
import BaseLoader from '@/components/BaseLoader.vue';
|
||||||
import QueryEditor from '@/components/QueryEditor';
|
import QueryEditor from '@/components/QueryEditor.vue';
|
||||||
import WorkspaceTabPropsSchedulerTimingModal from '@/components/WorkspaceTabPropsSchedulerTimingModal';
|
import WorkspaceTabPropsSchedulerTimingModal from '@/components/WorkspaceTabPropsSchedulerTimingModal.vue';
|
||||||
import Schedulers from '@/ipc-api/Schedulers';
|
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
import Schedulers from '@/ipc-api/Schedulers';
|
||||||
|
|
||||||
export default {
|
const { t } = useI18n();
|
||||||
name: 'WorkspaceTabPropsScheduler',
|
|
||||||
components: {
|
const props = defineProps({
|
||||||
BaseLoader,
|
|
||||||
QueryEditor,
|
|
||||||
WorkspaceTabPropsSchedulerTimingModal,
|
|
||||||
BaseSelect
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
tabUid: String,
|
tabUid: String,
|
||||||
connection: Object,
|
connection: Object,
|
||||||
scheduler: String,
|
scheduler: String,
|
||||||
isSelected: Boolean,
|
isSelected: Boolean,
|
||||||
schema: String
|
schema: String
|
||||||
},
|
});
|
||||||
setup () {
|
|
||||||
const { addNotification } = useNotificationsStore();
|
const { addNotification } = useNotificationsStore();
|
||||||
const workspacesStore = useWorkspacesStore();
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
|
||||||
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getWorkspace,
|
getWorkspace,
|
||||||
refreshStructure,
|
refreshStructure,
|
||||||
renameTabs,
|
renameTabs,
|
||||||
newTab,
|
|
||||||
changeBreadcrumbs,
|
changeBreadcrumbs,
|
||||||
setUnsavedChanges
|
setUnsavedChanges
|
||||||
} = workspacesStore;
|
} = workspacesStore;
|
||||||
|
|
||||||
return {
|
const queryEditor: Ref<Component & {editor: Ace.Editor; $el: HTMLElement}> = ref(null);
|
||||||
addNotification,
|
const isLoading = ref(false);
|
||||||
selectedWorkspace,
|
const isSaving = ref(false);
|
||||||
getWorkspace,
|
const isTimingModal = ref(false);
|
||||||
refreshStructure,
|
const originalScheduler: Ref<EventInfos> = ref(null);
|
||||||
renameTabs,
|
const localScheduler: Ref<EventInfos> = ref({} as EventInfos);
|
||||||
newTab,
|
const lastScheduler = ref(null);
|
||||||
changeBreadcrumbs,
|
const sqlProxy = ref('');
|
||||||
setUnsavedChanges
|
const editorHeight = ref(300);
|
||||||
};
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
isLoading: false,
|
|
||||||
isSaving: false,
|
|
||||||
isTimingModal: false,
|
|
||||||
originalScheduler: null,
|
|
||||||
localScheduler: { sql: '' },
|
|
||||||
lastScheduler: null,
|
|
||||||
sqlProxy: '',
|
|
||||||
editorHeight: 300
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
workspace () {
|
|
||||||
return this.getWorkspace(this.connection.uid);
|
|
||||||
},
|
|
||||||
isChanged () {
|
|
||||||
return JSON.stringify(this.originalScheduler) !== JSON.stringify(this.localScheduler);
|
|
||||||
},
|
|
||||||
isDefinerInUsers () {
|
|
||||||
return this.originalScheduler ? this.workspace.users.some(user => this.originalScheduler.definer === `\`${user.name}\`@\`${user.host}\``) : true;
|
|
||||||
},
|
|
||||||
schemaTables () {
|
|
||||||
const schemaTables = this.workspace.structure
|
|
||||||
.filter(schema => schema.name === this.schema)
|
|
||||||
.map(schema => schema.tables);
|
|
||||||
|
|
||||||
return schemaTables.length ? schemaTables[0].filter(table => table.type === 'table') : [];
|
const workspace = computed(() => {
|
||||||
},
|
return getWorkspace(props.connection.uid);
|
||||||
users () {
|
});
|
||||||
const users = [{ value: '' }, ...this.workspace.users];
|
|
||||||
if (!this.isDefinerInUsers) {
|
const isChanged = computed(() => {
|
||||||
const [name, host] = this.originalScheduler.definer.replaceAll('`', '').split('@');
|
return JSON.stringify(originalScheduler.value) !== JSON.stringify(localScheduler.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
const isDefinerInUsers = computed(() => {
|
||||||
|
return originalScheduler.value ? workspace.value.users.some(user => originalScheduler.value.definer === `\`${user.name}\`@\`${user.host}\``) : true;
|
||||||
|
});
|
||||||
|
|
||||||
|
const users = computed(() => {
|
||||||
|
const users = [{ value: '' }, ...workspace.value.users];
|
||||||
|
if (!isDefinerInUsers.value) {
|
||||||
|
const [name, host] = originalScheduler.value.definer.replaceAll('`', '').split('@');
|
||||||
users.unshift({ name, host });
|
users.unshift({ name, host });
|
||||||
}
|
}
|
||||||
|
|
||||||
return users;
|
return users;
|
||||||
}
|
});
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
async schema () {
|
|
||||||
if (this.isSelected) {
|
|
||||||
await this.getSchedulerData();
|
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localScheduler.sql);
|
|
||||||
this.lastScheduler = this.scheduler;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async scheduler () {
|
|
||||||
if (this.isSelected) {
|
|
||||||
await this.getSchedulerData();
|
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localScheduler.sql);
|
|
||||||
this.lastScheduler = this.scheduler;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async isSelected (val) {
|
|
||||||
if (val) {
|
|
||||||
this.changeBreadcrumbs({ schema: this.schema, scheduler: this.scheduler });
|
|
||||||
|
|
||||||
setTimeout(() => {
|
const getSchedulerData = async () => {
|
||||||
this.resizeQueryEditor();
|
if (!props.scheduler) return;
|
||||||
}, 200);
|
|
||||||
|
|
||||||
if (this.lastScheduler !== this.scheduler)
|
isLoading.value = true;
|
||||||
this.getSchedulerData();
|
lastScheduler.value = props.scheduler;
|
||||||
}
|
|
||||||
},
|
|
||||||
isChanged (val) {
|
|
||||||
this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async created () {
|
|
||||||
await this.getSchedulerData();
|
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localScheduler.sql);
|
|
||||||
window.addEventListener('keydown', this.onKey);
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
window.addEventListener('resize', this.resizeQueryEditor);
|
|
||||||
},
|
|
||||||
unmounted () {
|
|
||||||
window.removeEventListener('resize', this.resizeQueryEditor);
|
|
||||||
},
|
|
||||||
beforeUnmount () {
|
|
||||||
window.removeEventListener('keydown', this.onKey);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async getSchedulerData () {
|
|
||||||
if (!this.scheduler) return;
|
|
||||||
|
|
||||||
this.isLoading = true;
|
|
||||||
this.lastScheduler = this.scheduler;
|
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: props.connection.uid,
|
||||||
schema: this.schema,
|
schema: props.schema,
|
||||||
scheduler: this.scheduler
|
scheduler: props.scheduler
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { status, response } = await Schedulers.getSchedulerInformations(params);
|
const { status, response } = await Schedulers.getSchedulerInformations(params);
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
this.originalScheduler = response;
|
originalScheduler.value = response;
|
||||||
this.localScheduler = JSON.parse(JSON.stringify(this.originalScheduler));
|
localScheduler.value = JSON.parse(JSON.stringify(originalScheduler.value));
|
||||||
this.sqlProxy = this.localScheduler.sql;
|
sqlProxy.value = localScheduler.value.sql;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
addNotification({ status: 'error', message: response });
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.resizeQueryEditor();
|
resizeQueryEditor();
|
||||||
this.isLoading = false;
|
isLoading.value = false;
|
||||||
},
|
};
|
||||||
async saveChanges () {
|
|
||||||
if (this.isSaving) return;
|
const saveChanges = async () => {
|
||||||
this.isSaving = true;
|
if (isSaving.value) return;
|
||||||
|
isSaving.value = true;
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: props.connection.uid,
|
||||||
scheduler: {
|
scheduler: {
|
||||||
...this.localScheduler,
|
...localScheduler.value,
|
||||||
schema: this.schema,
|
schema: props.schema,
|
||||||
oldName: this.originalScheduler.name
|
oldName: originalScheduler.value.name
|
||||||
}
|
} as AlterEventParams
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { status, response } = await Schedulers.alterScheduler(params);
|
const { status, response } = await Schedulers.alterScheduler(params);
|
||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
const oldName = this.originalScheduler.name;
|
const oldName = originalScheduler.value.name;
|
||||||
|
|
||||||
await this.refreshStructure(this.connection.uid);
|
await refreshStructure(props.connection.uid);
|
||||||
|
|
||||||
if (oldName !== this.localScheduler.name) {
|
if (oldName !== localScheduler.value.name) {
|
||||||
this.renameTabs({
|
renameTabs({
|
||||||
uid: this.connection.uid,
|
uid: props.connection.uid,
|
||||||
schema: this.schema,
|
schema: props.schema,
|
||||||
elementName: oldName,
|
elementName: oldName,
|
||||||
elementNewName: this.localScheduler.name,
|
elementNewName: localScheduler.value.name,
|
||||||
elementType: 'scheduler'
|
elementType: 'scheduler'
|
||||||
});
|
});
|
||||||
|
|
||||||
this.changeBreadcrumbs({ schema: this.schema, scheduler: this.localScheduler.name });
|
changeBreadcrumbs({ schema: props.schema, scheduler: localScheduler.value.name });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.getSchedulerData();
|
getSchedulerData();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
addNotification({ status: 'error', message: response });
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isSaving = false;
|
isSaving.value = false;
|
||||||
},
|
};
|
||||||
clearChanges () {
|
|
||||||
this.localScheduler = JSON.parse(JSON.stringify(this.originalScheduler));
|
const clearChanges = () => {
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localScheduler.sql);
|
localScheduler.value = JSON.parse(JSON.stringify(originalScheduler.value));
|
||||||
},
|
queryEditor.value.editor.session.setValue(localScheduler.value.sql);
|
||||||
resizeQueryEditor () {
|
};
|
||||||
if (this.$refs.queryEditor) {
|
|
||||||
|
const resizeQueryEditor = () => {
|
||||||
|
if (queryEditor.value) {
|
||||||
const footer = document.getElementById('footer');
|
const footer = document.getElementById('footer');
|
||||||
const size = window.innerHeight - this.$refs.queryEditor.$el.getBoundingClientRect().top - footer.offsetHeight;
|
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - footer.offsetHeight;
|
||||||
this.editorHeight = size;
|
editorHeight.value = size;
|
||||||
this.$refs.queryEditor.editor.resize();
|
queryEditor.value.editor.resize();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
showTimingModal () {
|
|
||||||
this.isTimingModal = true;
|
const showTimingModal = () => {
|
||||||
},
|
isTimingModal.value = true;
|
||||||
hideTimingModal () {
|
};
|
||||||
this.isTimingModal = false;
|
|
||||||
},
|
const hideTimingModal = () => {
|
||||||
timingUpdate (options) {
|
isTimingModal.value = false;
|
||||||
this.localScheduler = options;
|
};
|
||||||
},
|
|
||||||
onKey (e) {
|
const timingUpdate = (options: EventInfos) => {
|
||||||
if (this.isSelected) {
|
localScheduler.value = options;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onKey = (e: KeyboardEvent) => {
|
||||||
|
if (props.isSelected) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (e.ctrlKey && e.keyCode === 83) { // CTRL + S
|
if (e.ctrlKey && e.key === 's') { // CTRL + S
|
||||||
if (this.isChanged)
|
if (isChanged.value)
|
||||||
this.saveChanges();
|
saveChanges();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(() => props.schema, async () => {
|
||||||
|
if (props.isSelected) {
|
||||||
|
await getSchedulerData();
|
||||||
|
queryEditor.value.editor.session.setValue(localScheduler.value.sql);
|
||||||
|
lastScheduler.value = props.scheduler;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => props.scheduler, async () => {
|
||||||
|
if (props.isSelected) {
|
||||||
|
await getSchedulerData();
|
||||||
|
queryEditor.value.editor.session.setValue(localScheduler.value.sql);
|
||||||
|
lastScheduler.value = props.scheduler;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => props.isSelected, async (val) => {
|
||||||
|
if (val) {
|
||||||
|
changeBreadcrumbs({ schema: props.schema, scheduler: props.scheduler });
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
resizeQueryEditor();
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
if (lastScheduler.value !== props.scheduler)
|
||||||
|
getSchedulerData();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(isChanged, (val) => {
|
||||||
|
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: val });
|
||||||
|
});
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
await getSchedulerData();
|
||||||
|
queryEditor.value.editor.session.setValue(localScheduler.value.sql);
|
||||||
|
window.addEventListener('keydown', onKey);
|
||||||
|
})();
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('resize', resizeQueryEditor);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('resize', resizeQueryEditor);
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.removeEventListener('keydown', onKey);
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
:confirm-text="$t('word.confirm')"
|
:confirm-text="t('word.confirm')"
|
||||||
size="400"
|
size="400"
|
||||||
@confirm="confirmOptionsChange"
|
@confirm="confirmOptionsChange"
|
||||||
@hide="$emit('hide')"
|
@hide="$emit('hide')"
|
||||||
@ -8,14 +8,14 @@
|
|||||||
<template #header>
|
<template #header>
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-timer mr-1" />
|
<i class="mdi mdi-24px mdi-timer mr-1" />
|
||||||
<span class="cut-text">{{ $t('word.timing') }} "{{ localOptions.name }}"</span>
|
<span class="cut-text">{{ t('word.timing') }} "{{ localOptions.name }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #body>
|
<template #body>
|
||||||
<form class="form-horizontal">
|
<form class="form-horizontal">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label col-4">
|
<label class="form-label col-4">
|
||||||
{{ $t('word.execution') }}
|
{{ t('word.execution') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
@ -61,7 +61,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label col-4">
|
<label class="form-label col-4">
|
||||||
{{ $t('word.starts') }}
|
{{ t('word.starts') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
@ -82,7 +82,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label col-4">
|
<label class="form-label col-4">
|
||||||
{{ $t('word.ends') }}
|
{{ t('word.ends') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
@ -124,7 +124,7 @@
|
|||||||
<div class="col-4" />
|
<div class="col-4" />
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<label class="form-checkbox form-inline mt-2">
|
<label class="form-checkbox form-inline mt-2">
|
||||||
<input v-model="optionsProxy.preserve" type="checkbox"><i class="form-icon" /> {{ $t('message.preserveOnCompletion') }}
|
<input v-model="optionsProxy.preserve" type="checkbox"><i class="form-icon" /> {{ t('message.preserveOnCompletion') }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -133,52 +133,47 @@
|
|||||||
</ConfirmModal>
|
</ConfirmModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import moment from 'moment';
|
import { Ref, ref } from 'vue';
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
import * as moment from 'moment';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal.vue';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
import { EventInfos } from 'common/interfaces/antares';
|
||||||
|
|
||||||
export default {
|
const { t } = useI18n();
|
||||||
name: 'WorkspaceTabPropsSchedulerTimingModal',
|
|
||||||
components: {
|
const props = defineProps({
|
||||||
ConfirmModal,
|
|
||||||
BaseSelect
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
localOptions: Object,
|
localOptions: Object,
|
||||||
workspace: Object
|
workspace: Object
|
||||||
},
|
});
|
||||||
emits: ['hide', 'options-update'],
|
|
||||||
data () {
|
const emit = defineEmits(['hide', 'options-update']);
|
||||||
return {
|
|
||||||
optionsProxy: {},
|
const optionsProxy: Ref<EventInfos> = ref({} as EventInfos);
|
||||||
isOptionsChanging: false,
|
const hasStart = ref(false);
|
||||||
hasStart: false,
|
const hasEnd = ref(false);
|
||||||
hasEnd: false
|
|
||||||
|
const confirmOptionsChange = () => {
|
||||||
|
if (!hasStart.value) optionsProxy.value.starts = '';
|
||||||
|
if (!hasEnd.value) optionsProxy.value.ends = '';
|
||||||
|
|
||||||
|
emit('options-update', optionsProxy.value);
|
||||||
};
|
};
|
||||||
},
|
|
||||||
created () {
|
|
||||||
this.optionsProxy = JSON.parse(JSON.stringify(this.localOptions));
|
|
||||||
|
|
||||||
this.hasStart = !!this.optionsProxy.starts;
|
const isNumberOrMinus = (event: KeyboardEvent) => {
|
||||||
this.hasEnd = !!this.optionsProxy.ends;
|
|
||||||
|
|
||||||
if (!this.optionsProxy.at) this.optionsProxy.at = moment().format('YYYY-MM-DD HH:mm:ss');
|
|
||||||
if (!this.optionsProxy.starts) this.optionsProxy.starts = moment().format('YYYY-MM-DD HH:mm:ss');
|
|
||||||
if (!this.optionsProxy.ends) this.optionsProxy.ends = moment().format('YYYY-MM-DD HH:mm:ss');
|
|
||||||
if (!this.optionsProxy.every.length) this.optionsProxy.every = ['1', 'DAY'];
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
confirmOptionsChange () {
|
|
||||||
if (!this.hasStart) this.optionsProxy.starts = '';
|
|
||||||
if (!this.hasEnd) this.optionsProxy.ends = '';
|
|
||||||
|
|
||||||
this.$emit('options-update', this.optionsProxy);
|
|
||||||
},
|
|
||||||
isNumberOrMinus (event) {
|
|
||||||
if (!/\d/.test(event.key) && event.key !== '-')
|
if (!/\d/.test(event.key) && event.key !== '-')
|
||||||
return event.preventDefault();
|
return event.preventDefault();
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
optionsProxy.value = JSON.parse(JSON.stringify(props.localOptions));
|
||||||
|
|
||||||
|
hasStart.value = !!optionsProxy.value.starts;
|
||||||
|
hasEnd.value = !!optionsProxy.value.ends;
|
||||||
|
|
||||||
|
if (!optionsProxy.value.at) optionsProxy.value.at = moment().format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
if (!optionsProxy.value.starts) optionsProxy.value.starts = moment().format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
if (!optionsProxy.value.ends) optionsProxy.value.ends = moment().format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
if (!optionsProxy.value.every.length) optionsProxy.value.every = ['1', 'DAY'];
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -11,16 +11,16 @@
|
|||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
<span>{{ $t('word.save') }}</span>
|
<span>{{ t('word.save') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
:disabled="!isChanged || isSaving"
|
:disabled="!isChanged || isSaving"
|
||||||
class="btn btn-link btn-sm mr-0"
|
class="btn btn-link btn-sm mr-0"
|
||||||
:title="$t('message.clearChanges')"
|
:title="t('message.clearChanges')"
|
||||||
@click="clearChanges"
|
@click="clearChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
||||||
<span>{{ $t('word.clear') }}</span>
|
<span>{{ t('word.clear') }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="divider-vert py-3" />
|
<div class="divider-vert py-3" />
|
||||||
@ -28,20 +28,20 @@
|
|||||||
<button
|
<button
|
||||||
:disabled="isSaving"
|
:disabled="isSaving"
|
||||||
class="btn btn-dark btn-sm"
|
class="btn btn-dark btn-sm"
|
||||||
:title="$t('message.addNewField')"
|
:title="t('message.addNewField')"
|
||||||
@click="addField"
|
@click="addField"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-playlist-plus mr-1" />
|
<i class="mdi mdi-24px mdi-playlist-plus mr-1" />
|
||||||
<span>{{ $t('word.add') }}</span>
|
<span>{{ t('word.add') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
:disabled="isSaving"
|
:disabled="isSaving"
|
||||||
class="btn btn-dark btn-sm"
|
class="btn btn-dark btn-sm"
|
||||||
:title="$t('message.manageIndexes')"
|
:title="t('message.manageIndexes')"
|
||||||
@click="showIntdexesModal"
|
@click="showIntdexesModal"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-key mdi-rotate-45 mr-1" />
|
<i class="mdi mdi-24px mdi-key mdi-rotate-45 mr-1" />
|
||||||
<span>{{ $t('word.indexes') }}</span>
|
<span>{{ t('word.indexes') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-dark btn-sm"
|
class="btn btn-dark btn-sm"
|
||||||
@ -49,11 +49,11 @@
|
|||||||
@click="showForeignModal"
|
@click="showForeignModal"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-key-link mr-1" />
|
<i class="mdi mdi-24px mdi-key-link mr-1" />
|
||||||
<span>{{ $t('word.foreignKeys') }}</span>
|
<span>{{ t('word.foreignKeys') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="workspace-query-info">
|
<div class="workspace-query-info">
|
||||||
<div class="d-flex" :title="$t('word.schema')">
|
<div class="d-flex" :title="t('word.schema')">
|
||||||
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
|
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -63,7 +63,7 @@
|
|||||||
<div class="columns mb-4">
|
<div class="columns mb-4">
|
||||||
<div class="column col-auto">
|
<div class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">{{ $t('word.name') }}</label>
|
<label class="form-label">{{ t('word.name') }}</label>
|
||||||
<input
|
<input
|
||||||
v-model="localOptions.name"
|
v-model="localOptions.name"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
@ -73,7 +73,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-if="workspace.customizations.comment" class="column">
|
<div v-if="workspace.customizations.comment" class="column">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">{{ $t('word.comment') }}</label>
|
<label class="form-label">{{ t('word.comment') }}</label>
|
||||||
<input
|
<input
|
||||||
v-model="localOptions.comment"
|
v-model="localOptions.comment"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
@ -85,7 +85,7 @@
|
|||||||
<div v-if="workspace.customizations.autoIncrement" class="column col-auto">
|
<div v-if="workspace.customizations.autoIncrement" class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('word.autoIncrement') }}
|
{{ t('word.autoIncrement') }}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
ref="firstInput"
|
ref="firstInput"
|
||||||
@ -99,7 +99,7 @@
|
|||||||
<div v-if="workspace.customizations.collations" class="column col-auto">
|
<div v-if="workspace.customizations.collations" class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('word.collation') }}
|
{{ t('word.collation') }}
|
||||||
</label>
|
</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localOptions.collation"
|
v-model="localOptions.collation"
|
||||||
@ -113,7 +113,7 @@
|
|||||||
<div v-if="workspace.customizations.engines" class="column col-auto">
|
<div v-if="workspace.customizations.engines" class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('word.engine') }}
|
{{ t('word.engine') }}
|
||||||
</label>
|
</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localOptions.engine"
|
v-model="localOptions.engine"
|
||||||
@ -172,35 +172,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
|
import { Component, computed, onBeforeUnmount, Ref, ref, watch } from 'vue';
|
||||||
|
import { AlterTableParams, TableField, TableForeign, TableIndex, TableInfos, TableOptions } from 'common/interfaces/antares';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
import { useNotificationsStore } from '@/stores/notifications';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
import { uidGen } from 'common/libs/uidGen';
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
import Tables from '@/ipc-api/Tables';
|
import Tables from '@/ipc-api/Tables';
|
||||||
import BaseLoader from '@/components/BaseLoader';
|
import BaseLoader from '@/components/BaseLoader.vue';
|
||||||
import WorkspaceTabPropsTableFields from '@/components/WorkspaceTabPropsTableFields';
|
|
||||||
import WorkspaceTabPropsTableIndexesModal from '@/components/WorkspaceTabPropsTableIndexesModal';
|
|
||||||
import WorkspaceTabPropsTableForeignModal from '@/components/WorkspaceTabPropsTableForeignModal';
|
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
import WorkspaceTabPropsTableFields from '@/components/WorkspaceTabPropsTableFields.vue';
|
||||||
|
import WorkspaceTabPropsTableIndexesModal from '@/components/WorkspaceTabPropsTableIndexesModal.vue';
|
||||||
|
import WorkspaceTabPropsTableForeignModal from '@/components/WorkspaceTabPropsTableForeignModal.vue';
|
||||||
|
|
||||||
export default {
|
const { t } = useI18n();
|
||||||
name: 'WorkspaceTabPropsTable',
|
|
||||||
components: {
|
const props = defineProps({
|
||||||
BaseLoader,
|
|
||||||
WorkspaceTabPropsTableFields,
|
|
||||||
WorkspaceTabPropsTableIndexesModal,
|
|
||||||
WorkspaceTabPropsTableForeignModal,
|
|
||||||
BaseSelect
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
tabUid: String,
|
tabUid: String,
|
||||||
connection: Object,
|
connection: Object,
|
||||||
isSelected: Boolean,
|
isSelected: Boolean,
|
||||||
table: String,
|
table: String,
|
||||||
schema: String
|
schema: String
|
||||||
},
|
});
|
||||||
setup () {
|
|
||||||
const { addNotification } = useNotificationsStore();
|
const { addNotification } = useNotificationsStore();
|
||||||
const workspacesStore = useWorkspacesStore();
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
|
||||||
@ -215,127 +211,80 @@ export default {
|
|||||||
setUnsavedChanges
|
setUnsavedChanges
|
||||||
} = workspacesStore;
|
} = workspacesStore;
|
||||||
|
|
||||||
return {
|
const indexTable: Ref<Component & {tableWrapper: HTMLDivElement }> = ref(null);
|
||||||
addNotification,
|
const firstInput: Ref<HTMLInputElement> = ref(null);
|
||||||
getDatabaseVariable,
|
const isLoading = ref(false);
|
||||||
getWorkspace,
|
const isSaving = ref(false);
|
||||||
selectedWorkspace,
|
const isIndexesModal = ref(false);
|
||||||
refreshStructure,
|
const isForeignModal = ref(false);
|
||||||
setUnsavedChanges,
|
const originalFields: Ref<TableField[]> = ref([]);
|
||||||
renameTabs,
|
const localFields: Ref<TableField[]> = ref([]);
|
||||||
changeBreadcrumbs
|
const originalKeyUsage: Ref<TableForeign[]> = ref([]);
|
||||||
};
|
const localKeyUsage: Ref<TableForeign[]> = ref([]);
|
||||||
},
|
const originalIndexes: Ref<TableIndex[]> = ref([]);
|
||||||
data () {
|
const localIndexes: Ref<TableIndex[]> = ref([]);
|
||||||
return {
|
const tableOptions: Ref<TableOptions> = ref(null);
|
||||||
isLoading: false,
|
const localOptions: Ref<TableOptions> = ref({} as TableOptions);
|
||||||
isSaving: false,
|
const lastTable = ref(null);
|
||||||
isIndexesModal: false,
|
const newFieldsCounter = ref(0);
|
||||||
isForeignModal: false,
|
|
||||||
isOptionsChanging: false,
|
const workspace = computed(() => {
|
||||||
originalFields: [],
|
return getWorkspace(props.connection.uid);
|
||||||
localFields: [],
|
});
|
||||||
originalKeyUsage: [],
|
|
||||||
localKeyUsage: [],
|
const defaultCollation = computed(() => {
|
||||||
originalIndexes: [],
|
if (workspace.value.customizations.collations)
|
||||||
localIndexes: [],
|
return getDatabaseVariable(selectedWorkspace.value, 'collation_server')?.value || '';
|
||||||
tableOptions: {},
|
|
||||||
localOptions: {},
|
|
||||||
lastTable: null,
|
|
||||||
newFieldsCounter: 0
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
workspace () {
|
|
||||||
return this.getWorkspace(this.connection.uid);
|
|
||||||
},
|
|
||||||
defaultCollation () {
|
|
||||||
if (this.workspace.customizations.collations)
|
|
||||||
return this.getDatabaseVariable(this.selectedWorkspace, 'collation_server')?.value || '';
|
|
||||||
return '';
|
return '';
|
||||||
},
|
});
|
||||||
defaultEngine () {
|
|
||||||
const engine = this.getDatabaseVariable(this.connection.uid, 'default_storage_engine');
|
const schemaTables = computed(() => {
|
||||||
return engine ? engine.value : '';
|
const schemaTables = workspace.value.structure
|
||||||
},
|
.filter(schema => schema.name === props.schema)
|
||||||
schemaTables () {
|
|
||||||
const schemaTables = this.workspace.structure
|
|
||||||
.filter(schema => schema.name === this.schema)
|
|
||||||
.map(schema => schema.tables);
|
.map(schema => schema.tables);
|
||||||
|
|
||||||
return schemaTables.length ? schemaTables[0].filter(table => table.type === 'table') : [];
|
return schemaTables.length ? schemaTables[0].filter(table => table.type === 'table') : [];
|
||||||
},
|
});
|
||||||
isChanged () {
|
|
||||||
return JSON.stringify(this.originalFields) !== JSON.stringify(this.localFields) ||
|
|
||||||
JSON.stringify(this.originalKeyUsage) !== JSON.stringify(this.localKeyUsage) ||
|
|
||||||
JSON.stringify(this.originalIndexes) !== JSON.stringify(this.localIndexes) ||
|
|
||||||
JSON.stringify(this.tableOptions) !== JSON.stringify(this.localOptions);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
schema () {
|
|
||||||
if (this.isSelected) {
|
|
||||||
this.getFieldsData();
|
|
||||||
this.lastTable = this.table;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
table () {
|
|
||||||
if (this.isSelected) {
|
|
||||||
this.getFieldsData();
|
|
||||||
this.lastTable = this.table;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
isSelected (val) {
|
|
||||||
if (val) {
|
|
||||||
this.changeBreadcrumbs({ schema: this.schema, table: this.table });
|
|
||||||
|
|
||||||
if (this.lastTable !== this.table)
|
const isChanged = computed(() => {
|
||||||
this.getFieldsData();
|
return JSON.stringify(originalFields.value) !== JSON.stringify(localFields.value) ||
|
||||||
}
|
JSON.stringify(originalKeyUsage.value) !== JSON.stringify(localKeyUsage.value) ||
|
||||||
},
|
JSON.stringify(originalIndexes.value) !== JSON.stringify(localIndexes.value) ||
|
||||||
isChanged (val) {
|
JSON.stringify(tableOptions.value) !== JSON.stringify(localOptions.value);
|
||||||
this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
|
});
|
||||||
}
|
|
||||||
},
|
|
||||||
created () {
|
|
||||||
this.getFieldsData();
|
|
||||||
window.addEventListener('keydown', this.onKey);
|
|
||||||
},
|
|
||||||
beforeUnmount () {
|
|
||||||
window.removeEventListener('keydown', this.onKey);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async getTableOptions (params) {
|
|
||||||
const db = this.workspace.structure.find(db => db.name === this.schema);
|
|
||||||
|
|
||||||
if (db && db.tables.length && this.table)
|
const getTableOptions = async (params: {uid: string; schema: string; table: string}) => {
|
||||||
this.tableOptions = db.tables.find(table => table.name === this.table);
|
const db = workspace.value.structure.find(db => db.name === props.schema);
|
||||||
|
|
||||||
|
if (db && db.tables.length && props.table)
|
||||||
|
tableOptions.value = db.tables.find(table => table.name === props.table);
|
||||||
else {
|
else {
|
||||||
const { status, response } = await Tables.getTableOptions(params);
|
const { status, response } = await Tables.getTableOptions(params);
|
||||||
|
|
||||||
if (status === 'success')
|
if (status === 'success')
|
||||||
this.tableOptions = response;
|
tableOptions.value = response;
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
addNotification({ status: 'error', message: response });
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
async getFieldsData () {
|
|
||||||
if (!this.table) return;
|
|
||||||
|
|
||||||
this.localFields = [];
|
const getFieldsData = async () => {
|
||||||
this.lastTable = this.table;
|
if (!props.table) return;
|
||||||
this.newFieldsCounter = 0;
|
|
||||||
this.isLoading = true;
|
localFields.value = [];
|
||||||
|
lastTable.value = props.table;
|
||||||
|
newFieldsCounter.value = 0;
|
||||||
|
isLoading.value = true;
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: props.connection.uid,
|
||||||
schema: this.schema,
|
schema: props.schema,
|
||||||
table: this.table
|
table: props.table
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.getTableOptions(params);
|
await getTableOptions(params);
|
||||||
this.localOptions = JSON.parse(JSON.stringify(this.tableOptions));
|
localOptions.value = JSON.parse(JSON.stringify(tableOptions.value));
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@ -344,7 +293,7 @@ export default {
|
|||||||
try { // Columns data
|
try { // Columns data
|
||||||
const { status, response } = await Tables.getTableColumns(params);
|
const { status, response } = await Tables.getTableColumns(params);
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
this.originalFields = response.map(field => {
|
originalFields.value = response.map((field: TableField) => {
|
||||||
if (field.autoIncrement)
|
if (field.autoIncrement)
|
||||||
field.defaultType = 'autoincrement';
|
field.defaultType = 'autoincrement';
|
||||||
else if (field.default === null)
|
else if (field.default === null)
|
||||||
@ -361,30 +310,31 @@ export default {
|
|||||||
|
|
||||||
return { ...field, _antares_id: uidGen() };
|
return { ...field, _antares_id: uidGen() };
|
||||||
});
|
});
|
||||||
this.localFields = JSON.parse(JSON.stringify(this.originalFields));
|
localFields.value = JSON.parse(JSON.stringify(originalFields.value));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
addNotification({ status: 'error', message: response });
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
try { // Indexes
|
try { // Indexes
|
||||||
const { status, response } = await Tables.getTableIndexes(params);
|
const { status, response } = await Tables.getTableIndexes(params);
|
||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
const indexesObj = response.reduce((acc, curr) => {
|
const indexesObj = response.reduce((acc: {[key: string]: TableIndex[]}, curr: TableIndex) => {
|
||||||
acc[curr.name] = acc[curr.name] || [];
|
acc[curr.name] = acc[curr.name] || [];
|
||||||
acc[curr.name].push(curr);
|
acc[curr.name].push(curr);
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
this.originalIndexes = Object.keys(indexesObj).map(index => {
|
originalIndexes.value = Object.keys(indexesObj).map(index => {
|
||||||
return {
|
return {
|
||||||
_antares_id: uidGen(),
|
_antares_id: uidGen(),
|
||||||
name: index,
|
name: index,
|
||||||
fields: indexesObj[index].map(field => field.column),
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
fields: indexesObj[index].map((field: any) => field.column),
|
||||||
type: indexesObj[index][0].type,
|
type: indexesObj[index][0].type,
|
||||||
comment: indexesObj[index][0].comment,
|
comment: indexesObj[index][0].comment,
|
||||||
indexType: indexesObj[index][0].indexType,
|
indexType: indexesObj[index][0].indexType,
|
||||||
@ -393,91 +343,93 @@ export default {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
this.localIndexes = JSON.parse(JSON.stringify(this.originalIndexes));
|
localIndexes.value = JSON.parse(JSON.stringify(originalIndexes.value));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
addNotification({ status: 'error', message: response });
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
try { // Key usage (foreign keys)
|
try { // Key usage (foreign keys)
|
||||||
const { status, response } = await Tables.getKeyUsage(params);
|
const { status, response } = await Tables.getKeyUsage(params);
|
||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
this.originalKeyUsage = response.map(foreign => {
|
originalKeyUsage.value = response.map((foreign: TableForeign) => {
|
||||||
return {
|
return {
|
||||||
_antares_id: uidGen(),
|
_antares_id: uidGen(),
|
||||||
...foreign
|
...foreign
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
this.localKeyUsage = JSON.parse(JSON.stringify(this.originalKeyUsage));
|
localKeyUsage.value = JSON.parse(JSON.stringify(originalKeyUsage.value));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
addNotification({ status: 'error', message: response });
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isLoading = false;
|
isLoading.value = false;
|
||||||
},
|
};
|
||||||
async saveChanges () {
|
|
||||||
if (this.isSaving) return;
|
const saveChanges = async () => {
|
||||||
this.isSaving = true;
|
if (isSaving.value) return;
|
||||||
|
isSaving.value = true;
|
||||||
|
|
||||||
// FIELDS
|
// FIELDS
|
||||||
const originalIDs = this.originalFields.reduce((acc, curr) => [...acc, curr._antares_id], []);
|
const originalIDs = originalFields.value.reduce((acc, curr) => [...acc, curr._antares_id], []);
|
||||||
const localIDs = this.localFields.reduce((acc, curr) => [...acc, curr._antares_id], []);
|
const localIDs = localFields.value.reduce((acc, curr) => [...acc, curr._antares_id], []);
|
||||||
|
|
||||||
// Fields Additions
|
// Fields Additions
|
||||||
const additions = this.localFields.filter(field => !originalIDs.includes(field._antares_id)).map(field => {
|
const additions = localFields.value.filter(field => !originalIDs.includes(field._antares_id)).map(field => {
|
||||||
const lI = this.localFields.findIndex(localField => localField._antares_id === field._antares_id);
|
const lI = localFields.value.findIndex(localField => localField._antares_id === field._antares_id);
|
||||||
const after = lI > 0 ? this.localFields[lI - 1].name : false;
|
const after = lI > 0 ? localFields.value[lI - 1].name : false;
|
||||||
return { ...field, after };
|
return { ...field, after };
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fields Deletions
|
// Fields Deletions
|
||||||
const deletions = this.originalFields.filter(field => !localIDs.includes(field._antares_id));
|
const deletions = originalFields.value.filter(field => !localIDs.includes(field._antares_id));
|
||||||
|
|
||||||
// Fields Changes
|
// Fields Changes
|
||||||
const changes = [];
|
const changes: TableField[] & {after: string | boolean; orgName: string}[] = [];
|
||||||
this.localFields.forEach((field, i) => {
|
localFields.value.forEach((field, i) => {
|
||||||
const originalField = this.originalFields.find(oField => oField._antares_id === field._antares_id);
|
const originalField = originalFields.value.find(oField => oField._antares_id === field._antares_id);
|
||||||
if (!originalField) return;
|
if (!originalField) return;
|
||||||
const after = i > 0 ? this.localFields[i - 1].name : false;
|
const after = i > 0 ? localFields.value[i - 1].name : false;
|
||||||
const orgName = originalField.name;
|
const orgName = originalField.name;
|
||||||
|
|
||||||
changes.push({ ...field, after, orgName });
|
changes.push({ ...field, after, orgName });
|
||||||
});
|
});
|
||||||
|
|
||||||
// OPTIONS
|
// OPTIONS
|
||||||
const options = Object.keys(this.localOptions).reduce((acc, option) => {
|
const options = Object.keys(localOptions.value).reduce((acc: {[key:string]: TableInfos}, option: keyof TableInfos) => {
|
||||||
if (this.localOptions[option] !== this.tableOptions[option])
|
if (localOptions.value[option] !== tableOptions.value[option])
|
||||||
acc[option] = this.localOptions[option];
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
acc[option] = localOptions.value[option] as any;
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
// INDEXES
|
// INDEXES
|
||||||
const indexChanges = {
|
const indexChanges = {
|
||||||
additions: [],
|
additions: [] as TableIndex[],
|
||||||
changes: [],
|
changes: [] as TableIndex[],
|
||||||
deletions: []
|
deletions: [] as TableIndex[]
|
||||||
};
|
};
|
||||||
const originalIndexIDs = this.originalIndexes.reduce((acc, curr) => [...acc, curr._antares_id], []);
|
const originalIndexIDs = originalIndexes.value.reduce((acc, curr) => [...acc, curr._antares_id], []);
|
||||||
const localIndexIDs = this.localIndexes.reduce((acc, curr) => [...acc, curr._antares_id], []);
|
const localIndexIDs = localIndexes.value.reduce((acc, curr) => [...acc, curr._antares_id], []);
|
||||||
|
|
||||||
// Index Additions
|
// Index Additions
|
||||||
indexChanges.additions = this.localIndexes.filter(index => !originalIndexIDs.includes(index._antares_id));
|
indexChanges.additions = localIndexes.value.filter(index => !originalIndexIDs.includes(index._antares_id));
|
||||||
|
|
||||||
// Index Changes
|
// Index Changes
|
||||||
this.originalIndexes.forEach(originalIndex => {
|
originalIndexes.value.forEach(originalIndex => {
|
||||||
const lI = this.localIndexes.findIndex(localIndex => localIndex._antares_id === originalIndex._antares_id);
|
const lI = localIndexes.value.findIndex(localIndex => localIndex._antares_id === originalIndex._antares_id);
|
||||||
if (JSON.stringify(originalIndex) !== JSON.stringify(this.localIndexes[lI])) {
|
if (JSON.stringify(originalIndex) !== JSON.stringify(localIndexes.value[lI])) {
|
||||||
if (this.localIndexes[lI]) {
|
if (localIndexes.value[lI]) {
|
||||||
indexChanges.changes.push({
|
indexChanges.changes.push({
|
||||||
...this.localIndexes[lI],
|
...localIndexes.value[lI],
|
||||||
oldName: originalIndex.name,
|
oldName: originalIndex.name,
|
||||||
oldType: originalIndex.type
|
oldType: originalIndex.type
|
||||||
});
|
});
|
||||||
@ -486,27 +438,27 @@ export default {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Index Deletions
|
// Index Deletions
|
||||||
indexChanges.deletions = this.originalIndexes.filter(index => !localIndexIDs.includes(index._antares_id));
|
indexChanges.deletions = originalIndexes.value.filter(index => !localIndexIDs.includes(index._antares_id));
|
||||||
|
|
||||||
// FOREIGN KEYS
|
// FOREIGN KEYS
|
||||||
const foreignChanges = {
|
const foreignChanges = {
|
||||||
additions: [],
|
additions: [] as TableForeign[],
|
||||||
changes: [],
|
changes: [] as TableForeign[],
|
||||||
deletions: []
|
deletions: [] as TableForeign[]
|
||||||
};
|
};
|
||||||
const originalForeignIDs = this.originalKeyUsage.reduce((acc, curr) => [...acc, curr._antares_id], []);
|
const originalForeignIDs = originalKeyUsage.value.reduce((acc, curr) => [...acc, curr._antares_id], []);
|
||||||
const localForeignIDs = this.localKeyUsage.reduce((acc, curr) => [...acc, curr._antares_id], []);
|
const localForeignIDs = localKeyUsage.value.reduce((acc, curr) => [...acc, curr._antares_id], []);
|
||||||
|
|
||||||
// Foreigns Additions
|
// Foreigns Additions
|
||||||
foreignChanges.additions = this.localKeyUsage.filter(foreign => !originalForeignIDs.includes(foreign._antares_id));
|
foreignChanges.additions = localKeyUsage.value.filter(foreign => !originalForeignIDs.includes(foreign._antares_id));
|
||||||
|
|
||||||
// Foreigns Changes
|
// Foreigns Changes
|
||||||
this.originalKeyUsage.forEach(originalForeign => {
|
originalKeyUsage.value.forEach(originalForeign => {
|
||||||
const lI = this.localKeyUsage.findIndex(localForeign => localForeign._antares_id === originalForeign._antares_id);
|
const lI = localKeyUsage.value.findIndex(localForeign => localForeign._antares_id === originalForeign._antares_id);
|
||||||
if (JSON.stringify(originalForeign) !== JSON.stringify(this.localKeyUsage[lI])) {
|
if (JSON.stringify(originalForeign) !== JSON.stringify(localKeyUsage.value[lI])) {
|
||||||
if (this.localKeyUsage[lI]) {
|
if (localKeyUsage.value[lI]) {
|
||||||
foreignChanges.changes.push({
|
foreignChanges.changes.push({
|
||||||
...this.localKeyUsage[lI],
|
...localKeyUsage.value[lI],
|
||||||
oldName: originalForeign.constraintName
|
oldName: originalForeign.constraintName
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -514,18 +466,18 @@ export default {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Foreigns Deletions
|
// Foreigns Deletions
|
||||||
foreignChanges.deletions = this.originalKeyUsage.filter(foreign => !localForeignIDs.includes(foreign._antares_id));
|
foreignChanges.deletions = originalKeyUsage.value.filter(foreign => !localForeignIDs.includes(foreign._antares_id));
|
||||||
|
|
||||||
// ALTER
|
// ALTER
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: props.connection.uid,
|
||||||
schema: this.schema,
|
schema: props.schema,
|
||||||
table: this.table,
|
table: props.table,
|
||||||
tableStructure: {
|
tableStructure: {
|
||||||
name: this.localOptions.name,
|
name: localOptions.value.name,
|
||||||
fields: this.localFields,
|
fields: localFields.value,
|
||||||
foreigns: this.localKeyUsage,
|
foreigns: localKeyUsage.value,
|
||||||
indexes: this.localIndexes
|
indexes: localIndexes.value
|
||||||
},
|
},
|
||||||
additions,
|
additions,
|
||||||
changes,
|
changes,
|
||||||
@ -533,115 +485,122 @@ export default {
|
|||||||
indexChanges,
|
indexChanges,
|
||||||
foreignChanges,
|
foreignChanges,
|
||||||
options
|
options
|
||||||
};
|
} as unknown as AlterTableParams;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { status, response } = await Tables.alterTable(params);
|
const { status, response } = await Tables.alterTable(params);
|
||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
const oldName = this.tableOptions.name;
|
const oldName = tableOptions.value.name;
|
||||||
|
|
||||||
await this.refreshStructure(this.connection.uid);
|
await refreshStructure(props.connection.uid);
|
||||||
|
|
||||||
if (oldName !== this.localOptions.name) {
|
if (oldName !== localOptions.value.name) {
|
||||||
this.renameTabs({
|
renameTabs({
|
||||||
uid: this.connection.uid,
|
uid: props.connection.uid,
|
||||||
schema: this.schema,
|
schema: props.schema,
|
||||||
elementName: oldName,
|
elementName: oldName,
|
||||||
elementNewName: this.localOptions.name,
|
elementNewName: localOptions.value.name,
|
||||||
elementType: 'table'
|
elementType: 'table'
|
||||||
});
|
});
|
||||||
|
|
||||||
this.changeBreadcrumbs({ schema: this.schema, table: this.localOptions.name });
|
changeBreadcrumbs({ schema: props.schema, table: localOptions.value.name });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.getFieldsData();
|
getFieldsData();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
addNotification({ status: 'error', message: response });
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isSaving = false;
|
isSaving.value = false;
|
||||||
this.newFieldsCounter = 0;
|
newFieldsCounter.value = 0;
|
||||||
},
|
};
|
||||||
clearChanges () {
|
|
||||||
this.localFields = JSON.parse(JSON.stringify(this.originalFields));
|
const clearChanges = () => {
|
||||||
this.localIndexes = JSON.parse(JSON.stringify(this.originalIndexes));
|
localFields.value = JSON.parse(JSON.stringify(originalFields.value));
|
||||||
this.localKeyUsage = JSON.parse(JSON.stringify(this.originalKeyUsage));
|
localIndexes.value = JSON.parse(JSON.stringify(originalIndexes.value));
|
||||||
this.localOptions = JSON.parse(JSON.stringify(this.tableOptions));
|
localKeyUsage.value = JSON.parse(JSON.stringify(originalKeyUsage.value));
|
||||||
this.newFieldsCounter = 0;
|
localOptions.value = JSON.parse(JSON.stringify(tableOptions.value));
|
||||||
},
|
newFieldsCounter.value = 0;
|
||||||
addField () {
|
};
|
||||||
this.localFields.push({
|
|
||||||
|
const addField = () => {
|
||||||
|
localFields.value.push({
|
||||||
_antares_id: uidGen(),
|
_antares_id: uidGen(),
|
||||||
name: `${this.$tc('word.field', 1)}_${++this.newFieldsCounter}`,
|
name: `${t('word.field', 1)}_${++newFieldsCounter.value}`,
|
||||||
key: '',
|
key: '',
|
||||||
type: this.workspace.dataTypes[0].types[0].name,
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
schema: this.schema,
|
type: (workspace.value.dataTypes[0] as any).types[0].name,
|
||||||
table: this.table,
|
schema: props.schema,
|
||||||
numPrecision: null,
|
numPrecision: null,
|
||||||
numLength: this.workspace.dataTypes[0].types[0].length,
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
numLength: (workspace.value.dataTypes[0] as any).types[0].length,
|
||||||
datePrecision: null,
|
datePrecision: null,
|
||||||
charLength: null,
|
charLength: null,
|
||||||
nullable: false,
|
nullable: false,
|
||||||
unsigned: false,
|
unsigned: false,
|
||||||
zerofill: false,
|
zerofill: false,
|
||||||
order: this.localFields.length + 1,
|
order: localFields.value.length + 1,
|
||||||
default: null,
|
default: null,
|
||||||
charset: null,
|
charset: null,
|
||||||
collation: this.defaultCollation,
|
collation: defaultCollation.value,
|
||||||
autoIncrement: false,
|
autoIncrement: false,
|
||||||
onUpdate: '',
|
onUpdate: '',
|
||||||
comment: ''
|
comment: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const scrollable = this.$refs.indexTable.$refs.tableWrapper;
|
const scrollable = indexTable.value.tableWrapper;
|
||||||
scrollable.scrollTop = scrollable.scrollHeight + 30;
|
scrollable.scrollTop = scrollable.scrollHeight + 30;
|
||||||
}, 20);
|
}, 20);
|
||||||
},
|
};
|
||||||
renameField (payload) {
|
|
||||||
this.localIndexes = this.localIndexes.map(index => {
|
const renameField = (payload: {index: string; new: string; old: string}) => {
|
||||||
|
localIndexes.value = localIndexes.value.map(index => {
|
||||||
const fi = index.fields.findIndex(field => field === payload.old);
|
const fi = index.fields.findIndex(field => field === payload.old);
|
||||||
if (fi !== -1)
|
if (fi !== -1)
|
||||||
index.fields[fi] = payload.new;
|
index.fields[fi] = payload.new;
|
||||||
return index;
|
return index;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.localKeyUsage = this.localKeyUsage.map(key => {
|
localKeyUsage.value = localKeyUsage.value.map(key => {
|
||||||
if (key.field === payload.old)
|
if (key.field === payload.old)
|
||||||
key.field = payload.new;
|
key.field = payload.new;
|
||||||
return key;
|
return key;
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
duplicateField (uid) {
|
|
||||||
const fieldToClone = Object.assign({}, this.localFields.find(field => field._antares_id === uid));
|
const duplicateField = (uid: string) => {
|
||||||
|
const fieldToClone = Object.assign({}, localFields.value.find(field => field._antares_id === uid));
|
||||||
fieldToClone._antares_id = uidGen();
|
fieldToClone._antares_id = uidGen();
|
||||||
fieldToClone.name = `${fieldToClone.name}_copy`;
|
fieldToClone.name = `${fieldToClone.name}_copy`;
|
||||||
fieldToClone.order = this.localFields.length + 1;
|
fieldToClone.order = localFields.value.length + 1;
|
||||||
this.localFields = [...this.localFields, fieldToClone];
|
localFields.value = [...localFields.value, fieldToClone];
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const scrollable = this.$refs.indexTable.$refs.tableWrapper;
|
const scrollable = indexTable.value.tableWrapper;
|
||||||
scrollable.scrollTop = scrollable.scrollHeight + 30;
|
scrollable.scrollTop = scrollable.scrollHeight + 30;
|
||||||
}, 20);
|
}, 20);
|
||||||
},
|
};
|
||||||
removeField (uid) {
|
|
||||||
this.localFields = this.localFields.filter(field => field._antares_id !== uid);
|
const removeField = (uid: string) => {
|
||||||
this.localKeyUsage = this.localKeyUsage.filter(fk =>// Clear foreign keys
|
localFields.value = localFields.value.filter(field => field._antares_id !== uid);
|
||||||
this.localFields.some(field => field.name === fk.field)
|
localKeyUsage.value = localKeyUsage.value.filter(fk =>// Clear foreign keys
|
||||||
|
localFields.value.some(field => field.name === fk.field)
|
||||||
);
|
);
|
||||||
this.localIndexes = this.localIndexes.filter(index =>// Clear indexes
|
localIndexes.value = localIndexes.value.filter(index =>// Clear indexes
|
||||||
this.localFields.some(field =>
|
localFields.value.some(field =>
|
||||||
index.fields.includes(field.name)
|
index.fields.includes(field.name)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
},
|
};
|
||||||
addNewIndex (payload) {
|
|
||||||
this.localIndexes = [...this.localIndexes, {
|
const addNewIndex = (payload: { index: string; field: string }) => {
|
||||||
|
localIndexes.value = [...localIndexes.value, {
|
||||||
_antares_id: uidGen(),
|
_antares_id: uidGen(),
|
||||||
name: payload.index === 'PRIMARY' ? 'PRIMARY' : payload.field,
|
name: payload.index === 'PRIMARY' ? 'PRIMARY' : payload.field,
|
||||||
fields: [payload.field],
|
fields: [payload.field],
|
||||||
@ -651,43 +610,80 @@ export default {
|
|||||||
indexComment: '',
|
indexComment: '',
|
||||||
cardinality: 0
|
cardinality: 0
|
||||||
}];
|
}];
|
||||||
},
|
};
|
||||||
addToIndex (payload) {
|
|
||||||
this.localIndexes = this.localIndexes.map(index => {
|
const addToIndex = (payload: { index: string; field: string }) => {
|
||||||
|
localIndexes.value = localIndexes.value.map(index => {
|
||||||
if (index._antares_id === payload.index) index.fields.push(payload.field);
|
if (index._antares_id === payload.index) index.fields.push(payload.field);
|
||||||
return index;
|
return index;
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
optionsUpdate (options) {
|
|
||||||
this.localOptions = options;
|
const showIntdexesModal = () => {
|
||||||
},
|
isIndexesModal.value = true;
|
||||||
showIntdexesModal () {
|
};
|
||||||
this.isIndexesModal = true;
|
|
||||||
},
|
const hideIndexesModal = () => {
|
||||||
hideIndexesModal () {
|
isIndexesModal.value = false;
|
||||||
this.isIndexesModal = false;
|
};
|
||||||
},
|
|
||||||
indexesUpdate (indexes) {
|
const indexesUpdate = (indexes: TableIndex[]) => {
|
||||||
this.localIndexes = indexes;
|
localIndexes.value = indexes;
|
||||||
},
|
};
|
||||||
showForeignModal () {
|
|
||||||
this.isForeignModal = true;
|
const showForeignModal = () => {
|
||||||
},
|
isForeignModal.value = true;
|
||||||
hideForeignModal () {
|
};
|
||||||
this.isForeignModal = false;
|
|
||||||
},
|
const hideForeignModal = () => {
|
||||||
foreignsUpdate (foreigns) {
|
isForeignModal.value = false;
|
||||||
this.localKeyUsage = foreigns;
|
};
|
||||||
},
|
|
||||||
onKey (e) {
|
const foreignsUpdate = (foreigns: TableForeign[]) => {
|
||||||
if (this.isSelected) {
|
localKeyUsage.value = foreigns;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onKey = (e: KeyboardEvent) => {
|
||||||
|
if (props.isSelected) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (e.ctrlKey && e.keyCode === 83) { // CTRL + S
|
if (e.ctrlKey && e.key === 's') { // CTRL + S
|
||||||
if (this.isChanged)
|
if (isChanged.value)
|
||||||
this.saveChanges();
|
saveChanges();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(() => props.schema, () => {
|
||||||
|
if (props.isSelected) {
|
||||||
|
getFieldsData();
|
||||||
|
lastTable.value = props.table;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => props.table, () => {
|
||||||
|
if (props.isSelected) {
|
||||||
|
getFieldsData();
|
||||||
|
lastTable.value = props.table;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => props.isSelected, (val) => {
|
||||||
|
if (val) {
|
||||||
|
changeBreadcrumbs({ schema: props.schema, table: props.table });
|
||||||
|
|
||||||
|
if (lastTable.value !== props.table)
|
||||||
|
getFieldsData();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(isChanged, (val) => {
|
||||||
|
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: val });
|
||||||
|
});
|
||||||
|
|
||||||
|
getFieldsData();
|
||||||
|
window.addEventListener('keydown', onKey);
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.removeEventListener('keydown', onKey);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
@close-context="closeContext"
|
@close-context="closeContext"
|
||||||
>
|
>
|
||||||
<div class="context-element">
|
<div class="context-element">
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-key-plus text-light pr-1" /> {{ $t('message.createNewIndex') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-key-plus text-light pr-1" /> {{ t('message.createNewIndex') }}</span>
|
||||||
<i class="mdi mdi-18px mdi-chevron-right text-light pl-1" />
|
<i class="mdi mdi-18px mdi-chevron-right text-light pl-1" />
|
||||||
<div class="context-submenu">
|
<div class="context-submenu">
|
||||||
<div
|
<div
|
||||||
@ -19,7 +19,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="indexes.length" class="context-element">
|
<div v-if="indexes.length" class="context-element">
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-key-arrow-right text-light pr-1" /> {{ $t('message.addToIndex') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-key-arrow-right text-light pr-1" /> {{ t('message.addToIndex') }}</span>
|
||||||
<i class="mdi mdi-18px mdi-chevron-right text-light pl-1" />
|
<i class="mdi mdi-18px mdi-chevron-right text-light pl-1" />
|
||||||
<div class="context-submenu">
|
<div class="context-submenu">
|
||||||
<div
|
<div
|
||||||
@ -34,54 +34,54 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="context-element" @click="duplicateField">
|
<div class="context-element" @click="duplicateField">
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-content-duplicate text-light pr-1" /> {{ $t('word.duplicate') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-content-duplicate text-light pr-1" /> {{ t('word.duplicate') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="context-element" @click="deleteField">
|
<div class="context-element" @click="deleteField">
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-delete text-light pr-1" /> {{ $t('message.deleteField') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-delete text-light pr-1" /> {{ t('message.deleteField') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</BaseContextMenu>
|
</BaseContextMenu>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import BaseContextMenu from '@/components/BaseContextMenu';
|
import { computed, Prop } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import BaseContextMenu from '@/components/BaseContextMenu.vue';
|
||||||
|
import { TableIndex } from 'common/interfaces/antares';
|
||||||
|
|
||||||
export default {
|
const { t } = useI18n();
|
||||||
name: 'WorkspaceTabQueryTableContext',
|
|
||||||
components: {
|
const props = defineProps({
|
||||||
BaseContextMenu
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
contextEvent: MouseEvent,
|
contextEvent: MouseEvent,
|
||||||
indexes: Array,
|
indexes: Array as Prop<TableIndex[]>,
|
||||||
indexTypes: Array,
|
indexTypes: Array as Prop<string[]>,
|
||||||
selectedField: Object
|
selectedField: Object
|
||||||
},
|
});
|
||||||
emits: ['close-context', 'duplicate-selected', 'delete-selected', 'add-new-index', 'add-to-index'],
|
|
||||||
computed: {
|
const emit = defineEmits(['close-context', 'duplicate-selected', 'delete-selected', 'add-new-index', 'add-to-index']);
|
||||||
hasPrimary () {
|
|
||||||
return this.indexes.some(index => index.type === 'PRIMARY');
|
const hasPrimary = computed(() => props.indexes.some(index => index.type === 'PRIMARY'));
|
||||||
}
|
|
||||||
},
|
const closeContext = () => {
|
||||||
methods: {
|
emit('close-context');
|
||||||
closeContext () {
|
};
|
||||||
this.$emit('close-context');
|
|
||||||
},
|
const duplicateField = () => {
|
||||||
duplicateField () {
|
emit('duplicate-selected');
|
||||||
this.$emit('duplicate-selected');
|
closeContext();
|
||||||
this.closeContext();
|
};
|
||||||
},
|
|
||||||
deleteField () {
|
const deleteField = () => {
|
||||||
this.$emit('delete-selected');
|
emit('delete-selected');
|
||||||
this.closeContext();
|
closeContext();
|
||||||
},
|
};
|
||||||
addNewIndex (index) {
|
|
||||||
this.$emit('add-new-index', { field: this.selectedField.name, index });
|
const addNewIndex = (index: string) => {
|
||||||
this.closeContext();
|
emit('add-new-index', { field: props.selectedField.name, index });
|
||||||
},
|
closeContext();
|
||||||
addToIndex (index) {
|
};
|
||||||
this.$emit('add-to-index', { field: this.selectedField.name, index });
|
|
||||||
this.closeContext();
|
const addToIndex = (index: string) => {
|
||||||
}
|
emit('add-to-index', { field: props.selectedField.name, index });
|
||||||
}
|
closeContext();
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -13,89 +13,89 @@
|
|||||||
@delete-selected="removeField"
|
@delete-selected="removeField"
|
||||||
@duplicate-selected="duplicateField"
|
@duplicate-selected="duplicateField"
|
||||||
@close-context="isContext = false"
|
@close-context="isContext = false"
|
||||||
@add-new-index="$emit('add-new-index', $event)"
|
@add-new-index="emit('add-new-index', $event)"
|
||||||
@add-to-index="$emit('add-to-index', $event)"
|
@add-to-index="emit('add-to-index', $event)"
|
||||||
/>
|
/>
|
||||||
<div ref="propTable" class="table table-hover">
|
<div ref="propTable" class="table table-hover">
|
||||||
<div class="thead">
|
<div class="thead">
|
||||||
<div class="tr">
|
<div class="tr">
|
||||||
<div class="th">
|
<div class="th">
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
{{ $t('word.order') }}
|
{{ t('word.order') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="th">
|
<div class="th">
|
||||||
<div class="table-column-title">
|
<div class="table-column-title">
|
||||||
{{ $tc('word.key', 2) }}
|
{{ t('word.key', 2) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="th">
|
<div class="th">
|
||||||
<div class="column-resizable min-100">
|
<div class="column-resizable min-100">
|
||||||
<div class="table-column-title">
|
<div class="table-column-title">
|
||||||
{{ $t('word.name') }}
|
{{ t('word.name') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="th">
|
<div class="th">
|
||||||
<div class="column-resizable min-100">
|
<div class="column-resizable min-100">
|
||||||
<div class="table-column-title">
|
<div class="table-column-title">
|
||||||
{{ $t('word.type') }}
|
{{ t('word.type') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="customizations.tableArray" class="th">
|
<div v-if="customizations.tableArray" class="th">
|
||||||
<div class="column-resizable">
|
<div class="column-resizable">
|
||||||
<div class="table-column-title">
|
<div class="table-column-title">
|
||||||
{{ $t('word.array') }}
|
{{ t('word.array') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="th">
|
<div class="th">
|
||||||
<div class="column-resizable">
|
<div class="column-resizable">
|
||||||
<div class="table-column-title">
|
<div class="table-column-title">
|
||||||
{{ $t('word.length') }}
|
{{ t('word.length') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="customizations.unsigned" class="th">
|
<div v-if="customizations.unsigned" class="th">
|
||||||
<div class="column-resizable">
|
<div class="column-resizable">
|
||||||
<div class="table-column-title">
|
<div class="table-column-title">
|
||||||
{{ $t('word.unsigned') }}
|
{{ t('word.unsigned') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="customizations.nullable" class="th">
|
<div v-if="customizations.nullable" class="th">
|
||||||
<div class="column-resizable">
|
<div class="column-resizable">
|
||||||
<div class="table-column-title">
|
<div class="table-column-title">
|
||||||
{{ $t('message.allowNull') }}
|
{{ t('message.allowNull') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="customizations.zerofill" class="th">
|
<div v-if="customizations.zerofill" class="th">
|
||||||
<div class="column-resizable">
|
<div class="column-resizable">
|
||||||
<div class="table-column-title">
|
<div class="table-column-title">
|
||||||
{{ $t('message.zeroFill') }}
|
{{ t('message.zeroFill') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="th">
|
<div class="th">
|
||||||
<div class="column-resizable">
|
<div class="column-resizable">
|
||||||
<div class="table-column-title">
|
<div class="table-column-title">
|
||||||
{{ $t('word.default') }}
|
{{ t('word.default') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="customizations.comment" class="th">
|
<div v-if="customizations.comment" class="th">
|
||||||
<div class="column-resizable">
|
<div class="column-resizable">
|
||||||
<div class="table-column-title">
|
<div class="table-column-title">
|
||||||
{{ $t('word.comment') }}
|
{{ t('word.comment') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="customizations.collation" class="th">
|
<div v-if="customizations.collation" class="th">
|
||||||
<div class="column-resizable min-100">
|
<div class="column-resizable min-100">
|
||||||
<div class="table-column-title">
|
<div class="table-column-title">
|
||||||
{{ $t('word.collation') }}
|
{{ t('word.collation') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -116,7 +116,7 @@
|
|||||||
:data-types="dataTypes"
|
:data-types="dataTypes"
|
||||||
:customizations="customizations"
|
:customizations="customizations"
|
||||||
@contextmenu="contextMenu"
|
@contextmenu="contextMenu"
|
||||||
@rename-field="$emit('rename-field', $event)"
|
@rename-field="emit('rename-field', $event)"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</Draggable>
|
</Draggable>
|
||||||
@ -124,135 +124,115 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>// TODO: expose tableWrapper
|
<script setup lang="ts">
|
||||||
import { storeToRefs } from 'pinia';
|
import { Component, computed, onMounted, onUnmounted, onUpdated, Prop, ref, Ref, watch } from 'vue';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
import Draggable from 'vuedraggable';
|
import * as Draggable from 'vuedraggable';
|
||||||
import TableRow from '@/components/WorkspaceTabPropsTableRow';
|
import TableRow from '@/components/WorkspaceTabPropsTableRow.vue';
|
||||||
import TableContext from '@/components/WorkspaceTabPropsTableContext';
|
import TableContext from '@/components/WorkspaceTabPropsTableContext.vue';
|
||||||
|
import { TableField, TableForeign, TableIndex } from 'common/interfaces/antares';
|
||||||
|
|
||||||
export default {
|
const { t } = useI18n();
|
||||||
name: 'WorkspaceTabPropsTableFields',
|
|
||||||
components: {
|
const props = defineProps({
|
||||||
TableRow,
|
fields: Array as Prop<TableField[]>,
|
||||||
TableContext,
|
indexes: Array as Prop<TableIndex[]>,
|
||||||
Draggable
|
foreigns: Array as Prop<TableForeign[]>,
|
||||||
},
|
indexTypes: Array as Prop<string[]>,
|
||||||
props: {
|
|
||||||
fields: Array,
|
|
||||||
indexes: Array,
|
|
||||||
foreigns: Array,
|
|
||||||
indexTypes: Array,
|
|
||||||
tabUid: [String, Number],
|
tabUid: [String, Number],
|
||||||
connUid: String,
|
connUid: String,
|
||||||
table: String,
|
table: String,
|
||||||
schema: String,
|
schema: String,
|
||||||
mode: String
|
mode: String
|
||||||
},
|
});
|
||||||
emits: ['add-new-index', 'add-to-index', 'rename-field', 'duplicate-field', 'remove-field'],
|
|
||||||
setup () {
|
|
||||||
const { addNotification } = useNotificationsStore();
|
|
||||||
const workspacesStore = useWorkspacesStore();
|
|
||||||
|
|
||||||
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
|
const emit = defineEmits(['add-new-index', 'add-to-index', 'rename-field', 'duplicate-field', 'remove-field']);
|
||||||
|
|
||||||
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
|
||||||
const { getWorkspace } = workspacesStore;
|
const { getWorkspace } = workspacesStore;
|
||||||
|
|
||||||
return {
|
const tableWrapper: Ref<HTMLDivElement> = ref(null);
|
||||||
addNotification,
|
const propTable: Ref<HTMLDivElement> = ref(null);
|
||||||
selectedWorkspace,
|
const resultTable: Ref<Component> = ref(null);
|
||||||
getWorkspace
|
const resultsSize = ref(1000);
|
||||||
};
|
const isContext = ref(false);
|
||||||
},
|
const contextEvent = ref(null);
|
||||||
data () {
|
const selectedField = ref(null);
|
||||||
return {
|
const scrollElement = ref(null);
|
||||||
resultsSize: 1000,
|
|
||||||
isContext: false,
|
|
||||||
contextEvent: null,
|
|
||||||
selectedField: null,
|
|
||||||
scrollElement: null
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
workspaceSchema () {
|
|
||||||
return this.getWorkspace(this.connUid).breadcrumbs.schema;
|
|
||||||
},
|
|
||||||
customizations () {
|
|
||||||
return this.getWorkspace(this.connUid).customizations;
|
|
||||||
},
|
|
||||||
dataTypes () {
|
|
||||||
return this.getWorkspace(this.connUid).dataTypes;
|
|
||||||
},
|
|
||||||
primaryField () {
|
|
||||||
return this.fields.filter(field => ['pri', 'uni'].includes(field.key))[0] || false;
|
|
||||||
},
|
|
||||||
tabProperties () {
|
|
||||||
return this.getWorkspaceTab(this.tabUid);
|
|
||||||
},
|
|
||||||
fieldsLength () {
|
|
||||||
return this.fields.length;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
fieldsLength () {
|
|
||||||
this.refreshScroller();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
updated () {
|
|
||||||
if (this.$refs.propTable)
|
|
||||||
this.refreshScroller();
|
|
||||||
|
|
||||||
if (this.$refs.tableWrapper)
|
const customizations = computed(() => getWorkspace(props.connUid).customizations);
|
||||||
this.scrollElement = this.$refs.tableWrapper;
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
},
|
const dataTypes = computed(() => getWorkspace(props.connUid).dataTypes) as any;
|
||||||
mounted () {
|
const fieldsLength = computed(() => props.fields.length);
|
||||||
window.addEventListener('resize', this.resizeResults);
|
|
||||||
},
|
const resizeResults = () => {
|
||||||
unmounted () {
|
if (resultTable.value) {
|
||||||
window.removeEventListener('resize', this.resizeResults);
|
const el = tableWrapper.value;
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
resizeResults () {
|
|
||||||
if (this.$refs.resultTable) {
|
|
||||||
const el = this.$refs.tableWrapper;
|
|
||||||
|
|
||||||
if (el) {
|
if (el) {
|
||||||
const footer = document.getElementById('footer');
|
const footer = document.getElementById('footer');
|
||||||
const size = window.innerHeight - el.getBoundingClientRect().top - footer.offsetHeight;
|
const size = window.innerHeight - el.getBoundingClientRect().top - footer.offsetHeight;
|
||||||
this.resultsSize = size;
|
resultsSize.value = size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
refreshScroller () {
|
|
||||||
this.resizeResults();
|
const refreshScroller = () => {
|
||||||
},
|
resizeResults();
|
||||||
contextMenu (event, uid) {
|
};
|
||||||
this.selectedField = this.fields.find(field => field._antares_id === uid);
|
|
||||||
this.contextEvent = event;
|
const contextMenu = (event: MouseEvent, uid: string) => {
|
||||||
this.isContext = true;
|
selectedField.value = props.fields.find(field => field._antares_id === uid);
|
||||||
},
|
contextEvent.value = event;
|
||||||
duplicateField () {
|
isContext.value = true;
|
||||||
this.$emit('duplicate-field', this.selectedField._antares_id);
|
};
|
||||||
},
|
|
||||||
removeField () {
|
const duplicateField = () => {
|
||||||
this.$emit('remove-field', this.selectedField._antares_id);
|
emit('duplicate-field', selectedField.value._antares_id);
|
||||||
},
|
};
|
||||||
getIndexes (field) {
|
|
||||||
return this.indexes.reduce((acc, curr) => {
|
const removeField = () => {
|
||||||
|
emit('remove-field', selectedField.value._antares_id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getIndexes = (field: string) => {
|
||||||
|
return props.indexes.reduce((acc, curr) => {
|
||||||
acc.push(...curr.fields.map(f => ({ name: f, type: curr.type })));
|
acc.push(...curr.fields.map(f => ({ name: f, type: curr.type })));
|
||||||
return acc;
|
return acc;
|
||||||
}, []).filter(f => f.name === field);
|
}, []).filter(f => f.name === field);
|
||||||
},
|
};
|
||||||
getForeigns (field) {
|
|
||||||
return this.foreigns.reduce((acc, curr) => {
|
const getForeigns = (field: string) => {
|
||||||
|
return props.foreigns.reduce((acc, curr) => {
|
||||||
if (curr.field === field)
|
if (curr.field === field)
|
||||||
acc.push(`${curr.refTable}.${curr.refField}`);
|
acc.push(`${curr.refTable}.${curr.refField}`);
|
||||||
return acc;
|
return acc;
|
||||||
}, []);
|
}, []);
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(fieldsLength, () => {
|
||||||
|
refreshScroller();
|
||||||
|
});
|
||||||
|
|
||||||
|
onUpdated(() => {
|
||||||
|
if (propTable.value)
|
||||||
|
refreshScroller();
|
||||||
|
|
||||||
|
if (tableWrapper.value)
|
||||||
|
scrollElement.value = tableWrapper.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('resize', resizeResults);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('resize', resizeResults);
|
||||||
|
});
|
||||||
|
|
||||||
|
defineExpose({ tableWrapper });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
:confirm-text="$t('word.confirm')"
|
:confirm-text="t('word.confirm')"
|
||||||
size="medium"
|
size="medium"
|
||||||
class="options-modal"
|
class="options-modal"
|
||||||
@confirm="confirmForeignsChange"
|
@confirm="confirmForeignsChange"
|
||||||
@ -9,7 +9,7 @@
|
|||||||
<template #header>
|
<template #header>
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-key-link mr-1" />
|
<i class="mdi mdi-24px mdi-key-link mr-1" />
|
||||||
<span class="cut-text">{{ $t('word.foreignKeys') }} "{{ table }}"</span>
|
<span class="cut-text">{{ t('word.foreignKeys') }} "{{ table }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #body>
|
<template #body>
|
||||||
@ -20,16 +20,16 @@
|
|||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<button class="btn btn-dark btn-sm d-flex" @click="addForeign">
|
<button class="btn btn-dark btn-sm d-flex" @click="addForeign">
|
||||||
<i class="mdi mdi-24px mdi-link-plus mr-1" />
|
<i class="mdi mdi-24px mdi-link-plus mr-1" />
|
||||||
<span>{{ $t('word.add') }}</span>
|
<span>{{ t('word.add') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-dark btn-sm d-flex ml-2 mr-0"
|
class="btn btn-dark btn-sm d-flex ml-2 mr-0"
|
||||||
:title="$t('message.clearChanges')"
|
:title="t('message.clearChanges')"
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
@click.prevent="clearChanges"
|
@click.prevent="clearChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
||||||
<span>{{ $t('word.clear') }}</span>
|
<span>{{ t('word.clear') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -67,7 +67,7 @@
|
|||||||
<div class="tile-action">
|
<div class="tile-action">
|
||||||
<button
|
<button
|
||||||
class="btn btn-link remove-field p-0 mr-2"
|
class="btn btn-link remove-field p-0 mr-2"
|
||||||
:title="$t('word.delete')"
|
:title="t('word.delete')"
|
||||||
@click.prevent="removeIndex(foreign._antares_id)"
|
@click.prevent="removeIndex(foreign._antares_id)"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-close" />
|
<i class="mdi mdi-close" />
|
||||||
@ -86,7 +86,7 @@
|
|||||||
>
|
>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label col-3">
|
<label class="form-label col-3">
|
||||||
{{ $t('word.name') }}
|
{{ t('word.name') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<input
|
<input
|
||||||
@ -98,7 +98,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group mb-4">
|
<div class="form-group mb-4">
|
||||||
<label class="form-label col-3">
|
<label class="form-label col-3">
|
||||||
{{ $tc('word.field', 1) }}
|
{{ t('word.field', 1) }}
|
||||||
</label>
|
</label>
|
||||||
<div class="fields-list column pt-1">
|
<div class="fields-list column pt-1">
|
||||||
<label
|
<label
|
||||||
@ -114,7 +114,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label col-3 pt-0">
|
<label class="form-label col-3 pt-0">
|
||||||
{{ $t('message.referenceTable') }}
|
{{ t('message.referenceTable') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
@ -129,7 +129,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group mb-4">
|
<div class="form-group mb-4">
|
||||||
<label class="form-label col-3">
|
<label class="form-label col-3">
|
||||||
{{ $t('message.referenceField') }}
|
{{ t('message.referenceField') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="fields-list column pt-1">
|
<div class="fields-list column pt-1">
|
||||||
<label
|
<label
|
||||||
@ -145,7 +145,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label col-3">
|
<label class="form-label col-3">
|
||||||
{{ $t('message.onUpdate') }}
|
{{ t('message.onUpdate') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
@ -157,7 +157,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label col-3">
|
<label class="form-label col-3">
|
||||||
{{ $t('message.onDelete') }}
|
{{ t('message.onDelete') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
@ -174,11 +174,11 @@
|
|||||||
<i class="mdi mdi-key-link mdi-48px" />
|
<i class="mdi mdi-key-link mdi-48px" />
|
||||||
</div>
|
</div>
|
||||||
<p class="empty-title h5">
|
<p class="empty-title h5">
|
||||||
{{ $t('message.thereAreNoForeign') }}
|
{{ t('message.thereAreNoForeign') }}
|
||||||
</p>
|
</p>
|
||||||
<div class="empty-action">
|
<div class="empty-action">
|
||||||
<button class="btn btn-primary" @click="addForeign">
|
<button class="btn btn-primary" @click="addForeign">
|
||||||
{{ $t('message.createNewForeign') }}
|
{{ t('message.createNewForeign') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -188,176 +188,173 @@
|
|||||||
</ConfirmModal>
|
</ConfirmModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
|
import { computed, onMounted, onUnmounted, Prop, Ref, ref } from 'vue';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
import { useNotificationsStore } from '@/stores/notifications';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { uidGen } from 'common/libs/uidGen';
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
import Tables from '@/ipc-api/Tables';
|
import Tables from '@/ipc-api/Tables';
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
import ConfirmModal from '@/components/BaseConfirmModal.vue';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
import { TableField } from 'common/interfaces/antares';
|
||||||
|
|
||||||
export default {
|
const { t } = useI18n();
|
||||||
name: 'WorkspaceTabPropsTableForeignModal',
|
|
||||||
components: {
|
const props = defineProps({
|
||||||
ConfirmModal,
|
|
||||||
BaseSelect
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
localKeyUsage: Array,
|
localKeyUsage: Array,
|
||||||
connection: Object,
|
connection: Object,
|
||||||
table: String,
|
table: String,
|
||||||
schema: String,
|
schema: String,
|
||||||
schemaTables: Array,
|
schemaTables: Array,
|
||||||
fields: Array,
|
fields: Array as Prop<TableField[]>,
|
||||||
workspace: Object
|
workspace: Object
|
||||||
},
|
});
|
||||||
emits: ['foreigns-update', 'hide'],
|
|
||||||
setup () {
|
const emit = defineEmits(['foreigns-update', 'hide']);
|
||||||
|
|
||||||
const { addNotification } = useNotificationsStore();
|
const { addNotification } = useNotificationsStore();
|
||||||
|
|
||||||
return { addNotification };
|
const indexesPanel: Ref<HTMLDivElement> = ref(null);
|
||||||
},
|
const foreignProxy = ref([]);
|
||||||
data () {
|
const selectedForeignID = ref('');
|
||||||
return {
|
const modalInnerHeight = ref(400);
|
||||||
foreignProxy: [],
|
const refFields = ref({} as {[key: string]: TableField[]});
|
||||||
isOptionsChanging: false,
|
const foreignActions = [
|
||||||
selectedForeignID: '',
|
|
||||||
modalInnerHeight: 400,
|
|
||||||
refFields: {},
|
|
||||||
foreignActions: [
|
|
||||||
'RESTRICT',
|
'RESTRICT',
|
||||||
'CASCADE',
|
'CASCADE',
|
||||||
'SET NULL',
|
'SET NULL',
|
||||||
'NO ACTION'
|
'NO ACTION'
|
||||||
]
|
];
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
selectedForeignObj () {
|
|
||||||
return this.foreignProxy.find(foreign => foreign._antares_id === this.selectedForeignID);
|
|
||||||
},
|
|
||||||
isChanged () {
|
|
||||||
return JSON.stringify(this.localKeyUsage) !== JSON.stringify(this.foreignProxy);
|
|
||||||
},
|
|
||||||
hasPrimary () {
|
|
||||||
return this.foreignProxy.some(foreign => foreign.type === 'PRIMARY');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
this.foreignProxy = JSON.parse(JSON.stringify(this.localKeyUsage));
|
|
||||||
|
|
||||||
if (this.foreignProxy.length)
|
const selectedForeignObj = computed(() => foreignProxy.value.find(foreign => foreign._antares_id === selectedForeignID.value));
|
||||||
this.resetSelectedID();
|
const isChanged = computed(() => JSON.stringify(props.localKeyUsage) !== JSON.stringify(foreignProxy.value));
|
||||||
|
|
||||||
if (this.selectedForeignObj)
|
const confirmForeignsChange = () => {
|
||||||
this.getRefFields();
|
foreignProxy.value = foreignProxy.value.filter(foreign =>
|
||||||
|
|
||||||
this.getModalInnerHeight();
|
|
||||||
window.addEventListener('resize', this.getModalInnerHeight);
|
|
||||||
},
|
|
||||||
unmounted () {
|
|
||||||
window.removeEventListener('resize', this.getModalInnerHeight);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
confirmForeignsChange () {
|
|
||||||
this.foreignProxy = this.foreignProxy.filter(foreign =>
|
|
||||||
foreign.field &&
|
foreign.field &&
|
||||||
foreign.refField &&
|
foreign.refField &&
|
||||||
foreign.table &&
|
foreign.table &&
|
||||||
foreign.refTable
|
foreign.refTable
|
||||||
);
|
);
|
||||||
this.$emit('foreigns-update', this.foreignProxy);
|
emit('foreigns-update', foreignProxy.value);
|
||||||
},
|
};
|
||||||
selectForeign (event, id) {
|
|
||||||
if (this.selectedForeignID !== id && !event.target.classList.contains('remove-field')) {
|
const selectForeign = (event: MouseEvent, id: string) => {
|
||||||
this.selectedForeignID = id;
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
this.getRefFields();
|
if (selectedForeignID.value !== id && !(event.target as any).classList.contains('remove-field')) {
|
||||||
|
selectedForeignID.value = id;
|
||||||
|
getRefFields();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
getModalInnerHeight () {
|
|
||||||
|
const getModalInnerHeight = () => {
|
||||||
const modalBody = document.querySelector('.modal-body');
|
const modalBody = document.querySelector('.modal-body');
|
||||||
if (modalBody)
|
if (modalBody)
|
||||||
this.modalInnerHeight = modalBody.clientHeight - (parseFloat(getComputedStyle(modalBody).paddingTop) + parseFloat(getComputedStyle(modalBody).paddingBottom));
|
modalInnerHeight.value = modalBody.clientHeight - (parseFloat(getComputedStyle(modalBody).paddingTop) + parseFloat(getComputedStyle(modalBody).paddingBottom));
|
||||||
},
|
};
|
||||||
addForeign () {
|
|
||||||
this.foreignProxy = [...this.foreignProxy, {
|
const addForeign = () => {
|
||||||
|
foreignProxy.value = [...foreignProxy.value, {
|
||||||
_antares_id: uidGen(),
|
_antares_id: uidGen(),
|
||||||
constraintName: `FK_${uidGen()}`,
|
constraintName: `FK_${uidGen()}`,
|
||||||
refSchema: this.schema,
|
refSchema: props.schema,
|
||||||
table: this.table,
|
table: props.table,
|
||||||
refTable: '',
|
refTable: '',
|
||||||
field: '',
|
field: '',
|
||||||
refField: '',
|
refField: '',
|
||||||
onUpdate: this.foreignActions[0],
|
onUpdate: foreignActions[0],
|
||||||
onDelete: this.foreignActions[0]
|
onDelete: foreignActions[0]
|
||||||
}];
|
}];
|
||||||
|
|
||||||
if (this.foreignProxy.length === 1)
|
if (foreignProxy.value.length === 1)
|
||||||
this.resetSelectedID();
|
resetSelectedID();
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.$refs.indexesPanel.scrollTop = this.$refs.indexesPanel.scrollHeight + 60;
|
indexesPanel.value.scrollTop = indexesPanel.value.scrollHeight + 60;
|
||||||
}, 20);
|
}, 20);
|
||||||
},
|
};
|
||||||
removeIndex (id) {
|
|
||||||
this.foreignProxy = this.foreignProxy.filter(foreign => foreign._antares_id !== id);
|
|
||||||
|
|
||||||
if (this.selectedForeignID === id && this.foreignProxy.length)
|
const removeIndex = (id: string) => {
|
||||||
this.resetSelectedID();
|
foreignProxy.value = foreignProxy.value.filter(foreign => foreign._antares_id !== id);
|
||||||
},
|
|
||||||
clearChanges () {
|
if (selectedForeignID.value === id && foreignProxy.value.length)
|
||||||
this.foreignProxy = JSON.parse(JSON.stringify(this.localKeyUsage));
|
resetSelectedID();
|
||||||
if (!this.foreignProxy.some(foreign => foreign._antares_id === this.selectedForeignID))
|
};
|
||||||
this.resetSelectedID();
|
|
||||||
},
|
const clearChanges = () => {
|
||||||
toggleField (field) {
|
foreignProxy.value = JSON.parse(JSON.stringify(props.localKeyUsage));
|
||||||
this.foreignProxy = this.foreignProxy.map(foreign => {
|
if (!foreignProxy.value.some(foreign => foreign._antares_id === selectedForeignID.value))
|
||||||
if (foreign._antares_id === this.selectedForeignID)
|
resetSelectedID();
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleField = (field: string) => {
|
||||||
|
foreignProxy.value = foreignProxy.value.map(foreign => {
|
||||||
|
if (foreign._antares_id === selectedForeignID.value)
|
||||||
foreign.field = field;
|
foreign.field = field;
|
||||||
|
|
||||||
return foreign;
|
return foreign;
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
toggleRefField (field) {
|
|
||||||
this.foreignProxy = this.foreignProxy.map(foreign => {
|
const toggleRefField = (field: string) => {
|
||||||
if (foreign._antares_id === this.selectedForeignID)
|
foreignProxy.value = foreignProxy.value.map(foreign => {
|
||||||
|
if (foreign._antares_id === selectedForeignID.value)
|
||||||
foreign.refField = field;
|
foreign.refField = field;
|
||||||
|
|
||||||
return foreign;
|
return foreign;
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
resetSelectedID () {
|
|
||||||
this.selectedForeignID = this.foreignProxy.length ? this.foreignProxy[0]._antares_id : '';
|
const resetSelectedID = () => {
|
||||||
},
|
selectedForeignID.value = foreignProxy.value.length ? foreignProxy.value[0]._antares_id : '';
|
||||||
async getRefFields () {
|
};
|
||||||
if (!this.selectedForeignObj.refTable) return;
|
|
||||||
|
const getRefFields = async () => {
|
||||||
|
if (!selectedForeignObj.value.refTable) return;
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: props.connection.uid,
|
||||||
schema: this.selectedForeignObj.refSchema,
|
schema: selectedForeignObj.value.refSchema,
|
||||||
table: this.selectedForeignObj.refTable
|
table: selectedForeignObj.value.refTable
|
||||||
};
|
};
|
||||||
|
|
||||||
try { // Field data
|
try { // Field data
|
||||||
const { status, response } = await Tables.getTableColumns(params);
|
const { status, response } = await Tables.getTableColumns(params);
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
this.refFields = {
|
refFields.value = {
|
||||||
...this.refFields,
|
...refFields.value,
|
||||||
[this.selectedForeignID]: response
|
[selectedForeignID.value]: response
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
addNotification({ status: 'error', message: response });
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
|
||||||
},
|
|
||||||
reloadRefFields () {
|
|
||||||
this.selectedForeignObj.refField = '';
|
|
||||||
this.getRefFields();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const reloadRefFields = () => {
|
||||||
|
selectedForeignObj.value.refField = '';
|
||||||
|
getRefFields();
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
foreignProxy.value = JSON.parse(JSON.stringify(props.localKeyUsage));
|
||||||
|
|
||||||
|
if (foreignProxy.value.length)
|
||||||
|
resetSelectedID();
|
||||||
|
|
||||||
|
if (selectedForeignObj.value)
|
||||||
|
getRefFields();
|
||||||
|
|
||||||
|
getModalInnerHeight();
|
||||||
|
window.addEventListener('resize', getModalInnerHeight);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('resize', getModalInnerHeight);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@ -384,7 +381,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.fields-list {
|
.fields-list {
|
||||||
max-height: 80px;
|
max-height: 90px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
:confirm-text="$t('word.confirm')"
|
:confirm-text="t('word.confirm')"
|
||||||
size="medium"
|
size="medium"
|
||||||
class="options-modal"
|
class="options-modal"
|
||||||
@confirm="confirmIndexesChange"
|
@confirm="confirmIndexesChange"
|
||||||
@ -9,7 +9,7 @@
|
|||||||
<template #header>
|
<template #header>
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-key mdi-rotate-45 mr-1" />
|
<i class="mdi mdi-24px mdi-key mdi-rotate-45 mr-1" />
|
||||||
<span class="cut-text">{{ $t('word.indexes') }} "{{ table }}"</span>
|
<span class="cut-text">{{ t('word.indexes') }} "{{ table }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #body>
|
<template #body>
|
||||||
@ -20,16 +20,16 @@
|
|||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<button class="btn btn-dark btn-sm d-flex" @click="addIndex">
|
<button class="btn btn-dark btn-sm d-flex" @click="addIndex">
|
||||||
<i class="mdi mdi-24px mdi-key-plus mr-1" />
|
<i class="mdi mdi-24px mdi-key-plus mr-1" />
|
||||||
<span>{{ $t('word.add') }}</span>
|
<span>{{ t('word.add') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-dark btn-sm d-flex ml-2 mr-0"
|
class="btn btn-dark btn-sm d-flex ml-2 mr-0"
|
||||||
:title="$t('message.clearChanges')"
|
:title="t('message.clearChanges')"
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
@click.prevent="clearChanges"
|
@click.prevent="clearChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
||||||
<span>{{ $t('word.clear') }}</span>
|
<span>{{ t('word.clear') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -50,12 +50,12 @@
|
|||||||
<div class="tile-title">
|
<div class="tile-title">
|
||||||
{{ index.name }}
|
{{ index.name }}
|
||||||
</div>
|
</div>
|
||||||
<small class="tile-subtitle text-gray">{{ index.type }} · {{ index.fields.length }} {{ $tc('word.field', index.fields.length) }}</small>
|
<small class="tile-subtitle text-gray">{{ index.type }} · {{ index.fields.length }} {{ t('word.field', index.fields.length) }}</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="tile-action">
|
<div class="tile-action">
|
||||||
<button
|
<button
|
||||||
class="btn btn-link remove-field p-0 mr-2"
|
class="btn btn-link remove-field p-0 mr-2"
|
||||||
:title="$t('word.delete')"
|
:title="t('word.delete')"
|
||||||
@click.prevent="removeIndex(index._antares_id)"
|
@click.prevent="removeIndex(index._antares_id)"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-close" />
|
<i class="mdi mdi-close" />
|
||||||
@ -74,7 +74,7 @@
|
|||||||
>
|
>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label col-3">
|
<label class="form-label col-3">
|
||||||
{{ $t('word.name') }}
|
{{ t('word.name') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<input
|
<input
|
||||||
@ -86,20 +86,20 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label col-3">
|
<label class="form-label col-3">
|
||||||
{{ $t('word.type') }}
|
{{ t('word.type') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="selectedIndexObj.type"
|
v-model="selectedIndexObj.type"
|
||||||
:options="indexTypes"
|
:options="indexTypes"
|
||||||
:option-disabled="(opt) => opt === 'PRIMARY'"
|
:option-disabled="(opt: any) => opt === 'PRIMARY'"
|
||||||
class="form-select"
|
class="form-select"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label col-3">
|
<label class="form-label col-3">
|
||||||
{{ $tc('word.field', fields.length) }}
|
{{ t('word.field', fields.length) }}
|
||||||
</label>
|
</label>
|
||||||
<div class="fields-list column pt-1">
|
<div class="fields-list column pt-1">
|
||||||
<label
|
<label
|
||||||
@ -108,7 +108,7 @@
|
|||||||
class="form-checkbox m-0"
|
class="form-checkbox m-0"
|
||||||
@click.prevent="toggleField(field.name)"
|
@click.prevent="toggleField(field.name)"
|
||||||
>
|
>
|
||||||
<input type="checkbox" :checked="selectedIndexObj.fields.some(f => f === field.name)">
|
<input type="checkbox" :checked="selectedIndexObj.fields.some((f: string) => f === field.name)">
|
||||||
<i class="form-icon" /> {{ field.name }}
|
<i class="form-icon" /> {{ field.name }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@ -119,11 +119,11 @@
|
|||||||
<i class="mdi mdi-key-outline mdi-48px" />
|
<i class="mdi mdi-key-outline mdi-48px" />
|
||||||
</div>
|
</div>
|
||||||
<p class="empty-title h5">
|
<p class="empty-title h5">
|
||||||
{{ $t('message.thereAreNoIndexes') }}
|
{{ t('message.thereAreNoIndexes') }}
|
||||||
</p>
|
</p>
|
||||||
<div class="empty-action">
|
<div class="empty-action">
|
||||||
<button class="btn btn-primary" @click="addIndex">
|
<button class="btn btn-primary" @click="addIndex">
|
||||||
{{ $t('message.createNewIndex') }}
|
{{ t('message.createNewIndex') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -133,72 +133,53 @@
|
|||||||
</ConfirmModal>
|
</ConfirmModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
|
import { computed, onMounted, onUnmounted, Prop, Ref, ref } from 'vue';
|
||||||
|
import { TableField } from 'common/interfaces/antares';
|
||||||
import { uidGen } from 'common/libs/uidGen';
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal.vue';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
|
||||||
export default {
|
const { t } = useI18n();
|
||||||
name: 'WorkspaceTabPropsTableIndexesModal',
|
|
||||||
components: {
|
const props = defineProps({
|
||||||
ConfirmModal,
|
|
||||||
BaseSelect
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
localIndexes: Array,
|
localIndexes: Array,
|
||||||
table: String,
|
table: String,
|
||||||
fields: Array,
|
fields: Array as Prop<TableField[]>,
|
||||||
workspace: Object,
|
workspace: Object,
|
||||||
indexTypes: Array
|
indexTypes: Array
|
||||||
},
|
});
|
||||||
emits: ['hide', 'indexes-update'],
|
|
||||||
data () {
|
const emit = defineEmits(['hide', 'indexes-update']);
|
||||||
return {
|
|
||||||
indexesProxy: [],
|
const indexesPanel: Ref<HTMLDivElement> = ref(null);
|
||||||
isOptionsChanging: false,
|
const indexesProxy = ref([]);
|
||||||
selectedIndexID: '',
|
const selectedIndexID = ref('');
|
||||||
modalInnerHeight: 400
|
const modalInnerHeight = ref(400);
|
||||||
|
|
||||||
|
const selectedIndexObj = computed(() => indexesProxy.value.find(index => index._antares_id === selectedIndexID.value));
|
||||||
|
const isChanged = computed(() => JSON.stringify(props.localIndexes) !== JSON.stringify(indexesProxy.value));
|
||||||
|
|
||||||
|
const confirmIndexesChange = () => {
|
||||||
|
indexesProxy.value = indexesProxy.value.filter(index => index.fields.length);
|
||||||
|
emit('indexes-update', indexesProxy.value);
|
||||||
};
|
};
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
selectedIndexObj () {
|
|
||||||
return this.indexesProxy.find(index => index._antares_id === this.selectedIndexID);
|
|
||||||
},
|
|
||||||
isChanged () {
|
|
||||||
return JSON.stringify(this.localIndexes) !== JSON.stringify(this.indexesProxy);
|
|
||||||
},
|
|
||||||
hasPrimary () {
|
|
||||||
return this.indexesProxy.some(index => index.type === 'PRIMARY');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
this.indexesProxy = JSON.parse(JSON.stringify(this.localIndexes));
|
|
||||||
|
|
||||||
if (this.indexesProxy.length)
|
const selectIndex = (event: MouseEvent, id: string) => {
|
||||||
this.resetSelectedID();
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
if (selectedIndexID.value !== id && !(event.target as any).classList.contains('remove-field'))
|
||||||
|
selectedIndexID.value = id;
|
||||||
|
};
|
||||||
|
|
||||||
this.getModalInnerHeight();
|
const getModalInnerHeight = () => {
|
||||||
window.addEventListener('resize', this.getModalInnerHeight);
|
|
||||||
},
|
|
||||||
unmounted () {
|
|
||||||
window.removeEventListener('resize', this.getModalInnerHeight);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
confirmIndexesChange () {
|
|
||||||
this.indexesProxy = this.indexesProxy.filter(index => index.fields.length);
|
|
||||||
this.$emit('indexes-update', this.indexesProxy);
|
|
||||||
},
|
|
||||||
selectIndex (event, id) {
|
|
||||||
if (this.selectedIndexID !== id && !event.target.classList.contains('remove-field'))
|
|
||||||
this.selectedIndexID = id;
|
|
||||||
},
|
|
||||||
getModalInnerHeight () {
|
|
||||||
const modalBody = document.querySelector('.modal-body');
|
const modalBody = document.querySelector('.modal-body');
|
||||||
if (modalBody)
|
if (modalBody)
|
||||||
this.modalInnerHeight = modalBody.clientHeight - (parseFloat(getComputedStyle(modalBody).paddingTop) + parseFloat(getComputedStyle(modalBody).paddingBottom));
|
modalInnerHeight.value = modalBody.clientHeight - (parseFloat(getComputedStyle(modalBody).paddingTop) + parseFloat(getComputedStyle(modalBody).paddingBottom));
|
||||||
},
|
};
|
||||||
addIndex () {
|
|
||||||
this.indexesProxy = [...this.indexesProxy, {
|
const addIndex = () => {
|
||||||
|
indexesProxy.value = [...indexesProxy.value, {
|
||||||
_antares_id: uidGen(),
|
_antares_id: uidGen(),
|
||||||
name: 'NEW_INDEX',
|
name: 'NEW_INDEX',
|
||||||
fields: [],
|
fields: [],
|
||||||
@ -209,40 +190,56 @@ export default {
|
|||||||
cardinality: 0
|
cardinality: 0
|
||||||
}];
|
}];
|
||||||
|
|
||||||
if (this.indexesProxy.length === 1)
|
if (indexesProxy.value.length === 1)
|
||||||
this.resetSelectedID();
|
resetSelectedID();
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.$refs.indexesPanel.scrollTop = this.$refs.indexesPanel.scrollHeight + 60;
|
indexesPanel.value.scrollTop = indexesPanel.value.scrollHeight + 60;
|
||||||
}, 20);
|
}, 20);
|
||||||
},
|
};
|
||||||
removeIndex (id) {
|
|
||||||
this.indexesProxy = this.indexesProxy.filter(index => index._antares_id !== id);
|
|
||||||
|
|
||||||
if (this.selectedIndexID === id && this.indexesProxy.length)
|
const removeIndex = (id: string) => {
|
||||||
this.resetSelectedID();
|
indexesProxy.value = indexesProxy.value.filter(index => index._antares_id !== id);
|
||||||
},
|
|
||||||
clearChanges () {
|
if (selectedIndexID.value === id && indexesProxy.value.length)
|
||||||
this.indexesProxy = JSON.parse(JSON.stringify(this.localIndexes));
|
resetSelectedID();
|
||||||
if (!this.indexesProxy.some(index => index._antares_id === this.selectedIndexID))
|
};
|
||||||
this.resetSelectedID();
|
|
||||||
},
|
const clearChanges = () => {
|
||||||
toggleField (field) {
|
indexesProxy.value = JSON.parse(JSON.stringify(props.localIndexes));
|
||||||
this.indexesProxy = this.indexesProxy.map(index => {
|
if (!indexesProxy.value.some(index => index._antares_id === selectedIndexID.value))
|
||||||
if (index._antares_id === this.selectedIndexID) {
|
resetSelectedID();
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleField = (field: string) => {
|
||||||
|
indexesProxy.value = indexesProxy.value.map(index => {
|
||||||
|
if (index._antares_id === selectedIndexID.value) {
|
||||||
if (index.fields.includes(field))
|
if (index.fields.includes(field))
|
||||||
index.fields = index.fields.filter(f => f !== field);
|
index.fields = index.fields.filter((f: string) => f !== field);
|
||||||
else
|
else
|
||||||
index.fields.push(field);
|
index.fields.push(field);
|
||||||
}
|
}
|
||||||
return index;
|
return index;
|
||||||
});
|
});
|
||||||
},
|
|
||||||
resetSelectedID () {
|
|
||||||
this.selectedIndexID = this.indexesProxy.length ? this.indexesProxy[0]._antares_id : '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const resetSelectedID = () => {
|
||||||
|
selectedIndexID.value = indexesProxy.value.length ? indexesProxy.value[0]._antares_id : '';
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
indexesProxy.value = JSON.parse(JSON.stringify(props.localIndexes));
|
||||||
|
|
||||||
|
if (indexesProxy.value.length)
|
||||||
|
resetSelectedID();
|
||||||
|
|
||||||
|
getModalInnerHeight();
|
||||||
|
window.addEventListener('resize', getModalInnerHeight);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('resize', getModalInnerHeight);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -219,7 +219,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
v-if="isDefaultModal"
|
v-if="isDefaultModal"
|
||||||
:confirm-text="$t('word.confirm')"
|
:confirm-text="t('word.confirm')"
|
||||||
size="400"
|
size="400"
|
||||||
@confirm="editOFF"
|
@confirm="editOFF"
|
||||||
@hide="hideDefaultModal"
|
@hide="hideDefaultModal"
|
||||||
@ -227,7 +227,7 @@
|
|||||||
<template #header>
|
<template #header>
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-playlist-edit mr-1" />
|
<i class="mdi mdi-24px mdi-playlist-edit mr-1" />
|
||||||
<span class="cut-text">{{ $t('word.default') }} "{{ row.name }}"</span>
|
<span class="cut-text">{{ t('word.default') }} "{{ row.name }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #body>
|
<template #body>
|
||||||
@ -250,7 +250,7 @@
|
|||||||
value="custom"
|
value="custom"
|
||||||
type="radio"
|
type="radio"
|
||||||
name="default"
|
name="default"
|
||||||
><i class="form-icon" /> {{ $t('message.customValue') }}
|
><i class="form-icon" /> {{ t('message.customValue') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<input
|
<input
|
||||||
@ -291,7 +291,7 @@
|
|||||||
type="radio"
|
type="radio"
|
||||||
name="default"
|
name="default"
|
||||||
value="expression"
|
value="expression"
|
||||||
><i class="form-icon" /> {{ $t('word.expression') }}
|
><i class="form-icon" /> {{ t('word.expression') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<input
|
<input
|
||||||
@ -306,7 +306,7 @@
|
|||||||
<div v-if="customizations.onUpdate">
|
<div v-if="customizations.onUpdate">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label col-4">
|
<label class="form-label col-4">
|
||||||
{{ $t('message.onUpdate') }}
|
{{ t('message.onUpdate') }}
|
||||||
</label>
|
</label>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<input
|
<input
|
||||||
@ -323,89 +323,78 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
|
import { computed, nextTick, onMounted, Prop, PropType, Ref, ref, watch } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
import ConfirmModal from '@/components/BaseConfirmModal.vue';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
import { TableField, TableIndex, TypesGroup } from 'common/interfaces/antares';
|
||||||
|
|
||||||
export default {
|
const { t } = useI18n();
|
||||||
name: 'WorkspaceTabPropsTableRow',
|
|
||||||
components: {
|
const props = defineProps({
|
||||||
ConfirmModal,
|
row: Object as Prop<TableField>,
|
||||||
BaseSelect
|
dataTypes: {
|
||||||
|
type: Array as PropType<TypesGroup[]>,
|
||||||
|
default: () => []
|
||||||
},
|
},
|
||||||
props: {
|
indexes: Array as Prop<TableIndex[]>,
|
||||||
row: Object,
|
foreigns: Array as Prop<string[]>,
|
||||||
dataTypes: { type: Array, default: () => [] },
|
|
||||||
indexes: Array,
|
|
||||||
foreigns: Array,
|
|
||||||
customizations: Object
|
customizations: Object
|
||||||
},
|
});
|
||||||
emits: ['contextmenu', 'rename-field'],
|
|
||||||
setup () {
|
const emit = defineEmits(['contextmenu', 'rename-field']);
|
||||||
const { addNotification } = useNotificationsStore();
|
|
||||||
const workspacesStore = useWorkspacesStore();
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
|
||||||
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
|
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
|
||||||
|
|
||||||
const { getWorkspace } = workspacesStore;
|
const { getWorkspace } = workspacesStore;
|
||||||
|
|
||||||
return {
|
const localRow: Ref<TableField> = ref({} as TableField);
|
||||||
addNotification,
|
const isInlineEditor: Ref<TableField> = ref({} as TableField);
|
||||||
selectedWorkspace,
|
const isDefaultModal = ref(false);
|
||||||
getWorkspace
|
const defaultValue = ref({
|
||||||
};
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
localRow: {},
|
|
||||||
isInlineEditor: {},
|
|
||||||
isDefaultModal: false,
|
|
||||||
defaultValue: {
|
|
||||||
type: 'noval',
|
type: 'noval',
|
||||||
custom: '',
|
custom: '',
|
||||||
expression: '',
|
expression: '',
|
||||||
onUpdate: ''
|
onUpdate: ''
|
||||||
},
|
});
|
||||||
editingContent: null,
|
const editingContent: Ref<string | number> = ref(null);
|
||||||
originalContent: null,
|
const originalContent = ref(null);
|
||||||
editingField: null
|
const editingField: Ref<keyof TableField> = ref(null);
|
||||||
};
|
|
||||||
},
|
const localLength = computed(() => {
|
||||||
computed: {
|
const localLength = localRow.value.numLength || localRow.value.charLength || localRow.value.datePrecision || localRow.value.numPrecision || 0 as number | true;
|
||||||
localLength () {
|
|
||||||
const localLength = this.localRow.numLength || this.localRow.charLength || this.localRow.datePrecision || this.localRow.numPrecision || 0;
|
|
||||||
return localLength === true ? null : localLength;
|
return localLength === true ? null : localLength;
|
||||||
},
|
});
|
||||||
fieldType () {
|
|
||||||
const fieldType = this.dataTypes.reduce((acc, group) => [...acc, ...group.types], []).filter(type =>
|
const fieldType = computed(() => {
|
||||||
type.name === (this.localRow.type ? this.localRow.type.toUpperCase() : '')
|
const fieldType = props.dataTypes.reduce((acc, group) => [...acc, ...group.types], []).filter(type =>
|
||||||
|
type.name === (localRow.value.type ? localRow.value.type.toUpperCase() : '')
|
||||||
);
|
);
|
||||||
const group = this.dataTypes.filter(group => group.types.some(type =>
|
const group = props.dataTypes.filter(group => group.types.some(type =>
|
||||||
type.name === (this.localRow.type ? this.localRow.type.toUpperCase() : ''))
|
type.name === (localRow.value.type ? localRow.value.type.toUpperCase() : ''))
|
||||||
);
|
);
|
||||||
|
|
||||||
return fieldType.length ? { ...fieldType[0], group: group[0].group } : {};
|
return fieldType.length ? { ...fieldType[0], group: group[0].group } : {};
|
||||||
},
|
});
|
||||||
fieldDefault () {
|
|
||||||
if (this.localRow.autoIncrement) return 'AUTO_INCREMENT';
|
const fieldDefault = computed(() => {
|
||||||
if (this.localRow.default === 'NULL') return 'NULL';
|
if (localRow.value.autoIncrement) return 'AUTO_INCREMENT';
|
||||||
return this.localRow.default;
|
if (localRow.value.default === 'NULL') return 'NULL';
|
||||||
},
|
return localRow.value.default;
|
||||||
collations () {
|
});
|
||||||
return this.getWorkspace(this.selectedWorkspace).collations;
|
|
||||||
},
|
const collations = computed(() => getWorkspace(selectedWorkspace.value).collations);
|
||||||
canAutoincrement () {
|
const canAutoincrement = computed(() => props.indexes.some(index => ['PRIMARY', 'UNIQUE'].includes(index.type)));
|
||||||
return this.indexes.some(index => ['PRIMARY', 'UNIQUE'].includes(index.type));
|
const isNullable = computed(() => props.customizations.nullablePrimary || !props.indexes.some(index => ['PRIMARY'].includes(index.type)));
|
||||||
},
|
|
||||||
isNullable () {
|
const isInDataTypes = computed(() => {
|
||||||
return this.customizations.nullablePrimary || !this.indexes.some(index => ['PRIMARY'].includes(index.type));
|
let typeNames: string[] = [];
|
||||||
},
|
for (const group of props.dataTypes) {
|
||||||
isInDataTypes () {
|
|
||||||
let typeNames = [];
|
|
||||||
for (const group of this.dataTypes) {
|
|
||||||
const groupTypeNames = group.types.reduce((acc, curr) => {
|
const groupTypeNames = group.types.reduce((acc, curr) => {
|
||||||
acc.push(curr.name);
|
acc.push(curr.name);
|
||||||
return acc;
|
return acc;
|
||||||
@ -413,195 +402,190 @@ export default {
|
|||||||
|
|
||||||
typeNames = [...groupTypeNames, ...typeNames];
|
typeNames = [...groupTypeNames, ...typeNames];
|
||||||
}
|
}
|
||||||
return typeNames.includes(this.row.type);
|
return typeNames.includes(props.row.type);
|
||||||
},
|
});
|
||||||
types () {
|
|
||||||
const types = [...this.dataTypes];
|
const types = computed(() => {
|
||||||
if (!this.isInDataTypes)
|
const types = [...props.dataTypes];
|
||||||
types.unshift({ name: this.row });
|
if (!isInDataTypes.value)
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
(types as any).unshift({ name: props.row });
|
||||||
|
|
||||||
return types;
|
return types;
|
||||||
}
|
});
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
localRow () {
|
|
||||||
this.initLocalRow();
|
|
||||||
},
|
|
||||||
row () {
|
|
||||||
this.localRow = this.row;
|
|
||||||
},
|
|
||||||
indexes () {
|
|
||||||
if (!this.canAutoincrement)
|
|
||||||
this.localRow.autoIncrement = false;
|
|
||||||
|
|
||||||
if (!this.isNullable)
|
const typeClass = (type: string) => {
|
||||||
this.localRow.nullable = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
this.localRow = this.row;
|
|
||||||
this.initLocalRow();
|
|
||||||
this.isInlineEditor.length = false;
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
keyName (key) {
|
|
||||||
switch (key) {
|
|
||||||
case 'pri':
|
|
||||||
return 'PRIMARY';
|
|
||||||
case 'uni':
|
|
||||||
return 'UNIQUE';
|
|
||||||
case 'mul':
|
|
||||||
return 'INDEX';
|
|
||||||
default:
|
|
||||||
return 'UNKNOWN ' + key;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
typeClass (type) {
|
|
||||||
if (type)
|
if (type)
|
||||||
return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`;
|
return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`;
|
||||||
return '';
|
return '';
|
||||||
},
|
};
|
||||||
initLocalRow () {
|
|
||||||
Object.keys(this.localRow).forEach(key => {
|
const initLocalRow = () => {
|
||||||
this.isInlineEditor[key] = false;
|
Object.keys(localRow.value).forEach(key => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
(isInlineEditor as any).value[key] = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.defaultValue.onUpdate = this.localRow.onUpdate;
|
defaultValue.value.onUpdate = localRow.value.onUpdate;
|
||||||
this.defaultValue.type = this.localRow.defaultType || 'noval';
|
defaultValue.value.type = localRow.value.defaultType || 'noval';
|
||||||
if (this.defaultValue.type === 'custom') {
|
if (defaultValue.value.type === 'custom') {
|
||||||
this.defaultValue.custom = this.localRow.default
|
defaultValue.value.custom = localRow.value.default
|
||||||
? this.localRow.default.includes('\'')
|
? localRow.value.default.includes('\'')
|
||||||
? this.localRow.default.split('\'')[1]
|
? localRow.value.default.split('\'')[1]
|
||||||
: this.localRow.default
|
: localRow.value.default
|
||||||
: '';
|
: '';
|
||||||
}
|
}
|
||||||
else if (this.defaultValue.type === 'expression') {
|
else if (defaultValue.value.type === 'expression') {
|
||||||
if (this.localRow.default.toUpperCase().includes('ON UPDATE'))
|
if (localRow.value.default.toUpperCase().includes('ON UPDATE'))
|
||||||
this.defaultValue.expression = this.localRow.default.replace(/ on update.*$/i, '');
|
defaultValue.value.expression = localRow.value.default.replace(/ on update.*$/i, '');
|
||||||
else
|
else
|
||||||
this.defaultValue.expression = this.localRow.default;
|
defaultValue.value.expression = localRow.value.default;
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
editON (event, content, field) {
|
|
||||||
if (field === 'length') {
|
|
||||||
if (['integer', 'float', 'binary', 'spatial'].includes(this.fieldType.group)) this.editingField = 'numLength';
|
|
||||||
else if (['string', 'unknown'].includes(this.fieldType.group)) this.editingField = 'charLength';
|
|
||||||
else if (['other'].includes(this.fieldType.group)) this.editingField = 'enumValues';
|
|
||||||
else if (['time'].includes(this.fieldType.group)) this.editingField = 'datePrecision';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
this.editingField = field;
|
|
||||||
|
|
||||||
if (this.localRow.enumValues && field === 'length') {
|
const editON = async (event: MouseEvent, content: string | number, field: keyof TableField) => {
|
||||||
this.editingContent = this.localRow.enumValues;
|
if (field === 'length') {
|
||||||
this.originalContent = this.localRow.enumValues;
|
if (['integer', 'float', 'binary', 'spatial'].includes(fieldType.value.group)) editingField.value = 'numLength';
|
||||||
|
else if (['string', 'unknown'].includes(fieldType.value.group)) editingField.value = 'charLength';
|
||||||
|
else if (['other'].includes(fieldType.value.group)) editingField.value = 'enumValues';
|
||||||
|
else if (['time'].includes(fieldType.value.group)) editingField.value = 'datePrecision';
|
||||||
}
|
}
|
||||||
else if (this.fieldType.scale && field === 'length') {
|
else
|
||||||
const scale = this.localRow.numScale !== null ? this.localRow.numScale : 0;
|
editingField.value = field;
|
||||||
this.editingContent = `${content}, ${scale}`;
|
|
||||||
this.originalContent = `${content}, ${scale}`;
|
if (localRow.value.enumValues && field === 'length') {
|
||||||
|
editingContent.value = localRow.value.enumValues;
|
||||||
|
originalContent.value = localRow.value.enumValues;
|
||||||
|
}
|
||||||
|
else if (fieldType.value.scale && field === 'length') {
|
||||||
|
const scale = localRow.value.numScale !== null ? localRow.value.numScale : 0;
|
||||||
|
editingContent.value = `${content}, ${scale}`;
|
||||||
|
originalContent.value = `${content}, ${scale}`;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.editingContent = content;
|
editingContent.value = content;
|
||||||
this.originalContent = content;
|
originalContent.value = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
const obj = { [field]: true };
|
const obj = { [field]: true };
|
||||||
this.isInlineEditor = { ...this.isInlineEditor, ...obj };
|
isInlineEditor.value = { ...isInlineEditor.value, ...obj };
|
||||||
|
|
||||||
if (field === 'default')
|
if (field === 'default')
|
||||||
this.isDefaultModal = true;
|
isDefaultModal.value = true;
|
||||||
else {
|
else {
|
||||||
this.$nextTick(() => { // Focus on input
|
await nextTick();
|
||||||
event.target.blur();
|
(event as MouseEvent & { target: HTMLInputElement }).target.blur();
|
||||||
|
await nextTick();
|
||||||
this.$nextTick(() => document.querySelector('.editable-field').focus());
|
document.querySelector<HTMLInputElement>('.editable-field').focus();
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
editOFF () {
|
|
||||||
if (this.editingField === 'name')
|
|
||||||
this.$emit('rename-field', { old: this.localRow[this.editingField], new: this.editingContent });
|
|
||||||
|
|
||||||
if (this.editingField === 'numLength' && this.fieldType.scale) {
|
const editOFF = () => {
|
||||||
const [length, scale] = this.editingContent.split(',');
|
if (editingField.value === 'name')
|
||||||
this.localRow.numLength = +length;
|
emit('rename-field', { old: localRow.value[editingField.value], new: editingContent.value });
|
||||||
this.localRow.numScale = scale ? +scale : null;
|
|
||||||
|
if (editingField.value === 'numLength' && fieldType.value.scale) {
|
||||||
|
const [length, scale] = (editingContent.value as string).split(',');
|
||||||
|
localRow.value.numLength = +length;
|
||||||
|
localRow.value.numScale = scale ? +scale : null;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.localRow[this.editingField] = this.editingContent;
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
(localRow.value as any)[editingField.value] = editingContent.value;
|
||||||
|
|
||||||
if (this.editingField === 'type' && this.editingContent !== this.originalContent) {
|
if (editingField.value === 'type' && editingContent.value !== originalContent.value) {
|
||||||
this.localRow.numLength = null;
|
localRow.value.numLength = null;
|
||||||
this.localRow.numScale = null;
|
localRow.value.numScale = null;
|
||||||
this.localRow.charLength = null;
|
localRow.value.charLength = null;
|
||||||
this.localRow.datePrecision = null;
|
localRow.value.datePrecision = null;
|
||||||
this.localRow.enumValues = '';
|
localRow.value.enumValues = '';
|
||||||
|
|
||||||
if (this.fieldType.length) {
|
if (fieldType.value.length) {
|
||||||
if (['integer', 'float', 'binary', 'spatial'].includes(this.fieldType.group)) this.localRow.numLength = 11;
|
if (['integer', 'float', 'binary', 'spatial'].includes(fieldType.value.group)) localRow.value.numLength = 11;
|
||||||
if (['string'].includes(this.fieldType.group)) this.localRow.charLength = 15;
|
if (['string'].includes(fieldType.value.group)) localRow.value.charLength = 15;
|
||||||
if (['time'].includes(this.fieldType.group)) this.localRow.datePrecision = 0;
|
if (['time'].includes(fieldType.value.group)) localRow.value.datePrecision = 0;
|
||||||
if (['other'].includes(this.fieldType.group)) this.localRow.enumValues = '\'valA\',\'valB\'';
|
if (['other'].includes(fieldType.value.group)) localRow.value.enumValues = '\'valA\',\'valB\'';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.fieldType.collation)
|
if (!fieldType.value.collation)
|
||||||
this.localRow.collation = null;
|
localRow.value.collation = null;
|
||||||
|
|
||||||
if (!this.fieldType.unsigned)
|
if (!fieldType.value.unsigned)
|
||||||
this.localRow.unsigned = false;
|
localRow.value.unsigned = false;
|
||||||
|
|
||||||
if (!this.fieldType.zerofill)
|
if (!fieldType.value.zerofill)
|
||||||
this.localRow.zerofill = false;
|
localRow.value.zerofill = false;
|
||||||
}
|
}
|
||||||
else if (this.editingField === 'default') {
|
else if (editingField.value === 'default') {
|
||||||
switch (this.defaultValue.type) {
|
switch (defaultValue.value.type) {
|
||||||
case 'autoincrement':
|
case 'autoincrement':
|
||||||
this.localRow.autoIncrement = true;
|
localRow.value.autoIncrement = true;
|
||||||
break;
|
break;
|
||||||
case 'noval':
|
case 'noval':
|
||||||
this.localRow.autoIncrement = false;
|
localRow.value.autoIncrement = false;
|
||||||
this.localRow.default = null;
|
localRow.value.default = null;
|
||||||
break;
|
break;
|
||||||
case 'null':
|
case 'null':
|
||||||
this.localRow.autoIncrement = false;
|
localRow.value.autoIncrement = false;
|
||||||
this.localRow.default = 'NULL';
|
localRow.value.default = 'NULL';
|
||||||
break;
|
break;
|
||||||
case 'custom':
|
case 'custom':
|
||||||
this.localRow.autoIncrement = false;
|
localRow.value.autoIncrement = false;
|
||||||
this.localRow.default = Number.isNaN(+this.defaultValue.custom) ? `'${this.defaultValue.custom}'` : this.defaultValue.custom;
|
localRow.value.default = Number.isNaN(+defaultValue.value.custom) ? `'${defaultValue.value.custom}'` : defaultValue.value.custom;
|
||||||
break;
|
break;
|
||||||
case 'expression':
|
case 'expression':
|
||||||
this.localRow.autoIncrement = false;
|
localRow.value.autoIncrement = false;
|
||||||
this.localRow.default = this.defaultValue.expression;
|
localRow.value.default = defaultValue.value.expression;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.localRow.onUpdate = this.defaultValue.onUpdate;
|
localRow.value.onUpdate = defaultValue.value.onUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.keys(this.isInlineEditor).forEach(key => {
|
Object.keys(isInlineEditor.value).forEach(key => {
|
||||||
this.isInlineEditor = { ...this.isInlineEditor, [key]: false };
|
isInlineEditor.value = { ...isInlineEditor.value, [key]: false };
|
||||||
});
|
});
|
||||||
|
|
||||||
this.editingContent = null;
|
editingContent.value = null;
|
||||||
this.originalContent = null;
|
originalContent.value = null;
|
||||||
this.editingField = null;
|
editingField.value = null;
|
||||||
},
|
};
|
||||||
checkLengthScale (e) {
|
|
||||||
e = (e) || window.event;
|
const checkLengthScale = (e: KeyboardEvent & { target: HTMLInputElement }) => {
|
||||||
const charCode = (e.which) ? e.which : e.keyCode;
|
e = (e) || window.event as KeyboardEvent & { target: HTMLInputElement };
|
||||||
|
const charCode = (e.which) ? e.which : e.code;
|
||||||
|
|
||||||
if (((charCode > 31 && (charCode < 48 || charCode > 57)) && charCode !== 44) || (charCode === 44 && e.target.value.includes(',')))
|
if (((charCode > 31 && (charCode < 48 || charCode > 57)) && charCode !== 44) || (charCode === 44 && e.target.value.includes(',')))
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
else
|
else
|
||||||
return true;
|
return true;
|
||||||
},
|
|
||||||
hideDefaultModal () {
|
|
||||||
this.isDefaultModal = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const hideDefaultModal = () => {
|
||||||
|
isDefaultModal.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(localRow, () => {
|
||||||
|
initLocalRow();
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => props.row, () => {
|
||||||
|
localRow.value = props.row;
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => props.indexes, () => {
|
||||||
|
if (!canAutoincrement.value)
|
||||||
|
localRow.value.autoIncrement = false;
|
||||||
|
|
||||||
|
if (!isNullable.value)
|
||||||
|
localRow.value.nullable = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
localRow.value = props.row;
|
||||||
|
initLocalRow();
|
||||||
|
isInlineEditor.value.length = false;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -11,20 +11,20 @@
|
|||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
<span>{{ $t('word.save') }}</span>
|
<span>{{ t('word.save') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
class="btn btn-link btn-sm mr-0"
|
class="btn btn-link btn-sm mr-0"
|
||||||
:title="$t('message.clearChanges')"
|
:title="t('message.clearChanges')"
|
||||||
@click="clearChanges"
|
@click="clearChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
||||||
<span>{{ $t('word.clear') }}</span>
|
<span>{{ t('word.clear') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="workspace-query-info">
|
<div class="workspace-query-info">
|
||||||
<div class="d-flex" :title="$t('word.schema')">
|
<div class="d-flex" :title="t('word.schema')">
|
||||||
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
|
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -34,7 +34,7 @@
|
|||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column col-auto">
|
<div class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">{{ $t('word.name') }}</label>
|
<label class="form-label">{{ t('word.name') }}</label>
|
||||||
<input
|
<input
|
||||||
v-model="localTrigger.name"
|
v-model="localTrigger.name"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
@ -44,12 +44,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-if="customizations.definer" class="column col-auto">
|
<div v-if="customizations.definer" class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">{{ $t('word.definer') }}</label>
|
<label class="form-label">{{ t('word.definer') }}</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localTrigger.definer"
|
v-model="localTrigger.definer"
|
||||||
:options="users"
|
:options="users"
|
||||||
:option-label="(user) => user.value === '' ? $t('message.currentUser') : `${user.name}@${user.host}`"
|
:option-label="(user: any) => user.value === '' ? t('message.currentUser') : `${user.name}@${user.host}`"
|
||||||
:option-track-by="(user) => user.value === '' ? '' : `\`${user.name}\`@\`${user.host}\``"
|
:option-track-by="(user: any) => user.value === '' ? '' : `\`${user.name}\`@\`${user.host}\``"
|
||||||
class="form-select"
|
class="form-select"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -57,7 +57,7 @@
|
|||||||
<fieldset class="column columns mb-0" :disabled="customizations.triggerOnlyRename">
|
<fieldset class="column columns mb-0" :disabled="customizations.triggerOnlyRename">
|
||||||
<div class="column col-auto">
|
<div class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">{{ $t('word.table') }}</label>
|
<label class="form-label">{{ t('word.table') }}</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localTrigger.table"
|
v-model="localTrigger.table"
|
||||||
:options="schemaTables"
|
:options="schemaTables"
|
||||||
@ -69,7 +69,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="column col-auto">
|
<div class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">{{ $t('word.event') }}</label>
|
<label class="form-label">{{ t('word.event') }}</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localTrigger.activation"
|
v-model="localTrigger.activation"
|
||||||
@ -85,7 +85,7 @@
|
|||||||
|
|
||||||
<div v-if="customizations.triggerMultipleEvents" class="px-4">
|
<div v-if="customizations.triggerMultipleEvents" class="px-4">
|
||||||
<label
|
<label
|
||||||
v-for="event in Object.keys(localEvents)"
|
v-for="event in (Object.keys(localEvents) as ('INSERT' | 'UPDATE' | 'DELETE')[])"
|
||||||
:key="event"
|
:key="event"
|
||||||
class="form-checkbox form-inline"
|
class="form-checkbox form-inline"
|
||||||
@change.prevent="changeEvents(event)"
|
@change.prevent="changeEvents(event)"
|
||||||
@ -101,7 +101,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
||||||
<BaseLoader v-if="isLoading" />
|
<BaseLoader v-if="isLoading" />
|
||||||
<label class="form-label ml-2">{{ $t('message.triggerStatement') }}</label>
|
<label class="form-label ml-2">{{ t('message.triggerStatement') }}</label>
|
||||||
<QueryEditor
|
<QueryEditor
|
||||||
v-show="isSelected"
|
v-show="isSelected"
|
||||||
ref="queryEditor"
|
ref="queryEditor"
|
||||||
@ -114,202 +114,143 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import { storeToRefs } from 'pinia';
|
import { Component, computed, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch } from 'vue';
|
||||||
|
import { Ace } from 'ace-builds';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
import { useNotificationsStore } from '@/stores/notifications';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
import QueryEditor from '@/components/QueryEditor';
|
import QueryEditor from '@/components/QueryEditor.vue';
|
||||||
import BaseLoader from '@/components/BaseLoader';
|
import BaseLoader from '@/components/BaseLoader.vue';
|
||||||
import Triggers from '@/ipc-api/Triggers';
|
import Triggers from '@/ipc-api/Triggers';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
|
||||||
export default {
|
type TriggerEventName = 'INSERT' | 'UPDATE' | 'DELETE'
|
||||||
name: 'WorkspaceTabPropsTrigger',
|
|
||||||
components: {
|
const { t } = useI18n();
|
||||||
BaseLoader,
|
|
||||||
QueryEditor,
|
const props = defineProps({
|
||||||
BaseSelect
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
tabUid: String,
|
tabUid: String,
|
||||||
connection: Object,
|
connection: Object,
|
||||||
trigger: String,
|
trigger: String,
|
||||||
isSelected: Boolean,
|
isSelected: Boolean,
|
||||||
schema: String
|
schema: String
|
||||||
},
|
});
|
||||||
setup () {
|
|
||||||
const { addNotification } = useNotificationsStore();
|
const { addNotification } = useNotificationsStore();
|
||||||
const workspacesStore = useWorkspacesStore();
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
|
||||||
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getWorkspace,
|
getWorkspace,
|
||||||
refreshStructure,
|
refreshStructure,
|
||||||
renameTabs,
|
renameTabs,
|
||||||
newTab,
|
|
||||||
changeBreadcrumbs,
|
changeBreadcrumbs,
|
||||||
setUnsavedChanges
|
setUnsavedChanges
|
||||||
} = workspacesStore;
|
} = workspacesStore;
|
||||||
|
|
||||||
return {
|
const queryEditor: Ref<Component & {editor: Ace.Editor; $el: HTMLElement}> = ref(null);
|
||||||
addNotification,
|
const lastTrigger = ref(null);
|
||||||
selectedWorkspace,
|
const isLoading = ref(false);
|
||||||
getWorkspace,
|
const isSaving = ref(false);
|
||||||
refreshStructure,
|
const originalTrigger = ref(null);
|
||||||
renameTabs,
|
const localTrigger = ref(null);
|
||||||
newTab,
|
const editorHeight = ref(300);
|
||||||
changeBreadcrumbs,
|
const localEvents = ref({ INSERT: false, UPDATE: false, DELETE: false });
|
||||||
setUnsavedChanges
|
|
||||||
};
|
const workspace = computed(() => {
|
||||||
},
|
return getWorkspace(props.connection.uid);
|
||||||
data () {
|
});
|
||||||
return {
|
|
||||||
isLoading: false,
|
const customizations = computed(() => {
|
||||||
isSaving: false,
|
return workspace.value.customizations;
|
||||||
originalTrigger: null,
|
});
|
||||||
localTrigger: { sql: '' },
|
|
||||||
lastTrigger: null,
|
const isChanged = computed(() => {
|
||||||
sqlProxy: '',
|
return JSON.stringify(originalTrigger.value) !== JSON.stringify(localTrigger.value);
|
||||||
editorHeight: 300,
|
});
|
||||||
localEvents: { INSERT: false, UPDATE: false, DELETE: false }
|
|
||||||
};
|
const isDefinerInUsers = computed(() => {
|
||||||
},
|
return originalTrigger.value ? workspace.value.users.some(user => originalTrigger.value.definer === `\`${user.name}\`@\`${user.host}\``) : true;
|
||||||
computed: {
|
});
|
||||||
workspace () {
|
|
||||||
return this.getWorkspace(this.connection.uid);
|
const schemaTables = computed(() => {
|
||||||
},
|
const schemaTables = workspace.value.structure
|
||||||
customizations () {
|
.filter(schema => schema.name === props.schema)
|
||||||
return this.workspace.customizations;
|
|
||||||
},
|
|
||||||
isChanged () {
|
|
||||||
return JSON.stringify(this.originalTrigger) !== JSON.stringify(this.localTrigger);
|
|
||||||
},
|
|
||||||
isDefinerInUsers () {
|
|
||||||
return this.originalTrigger ? this.workspace.users.some(user => this.originalTrigger.definer === `\`${user.name}\`@\`${user.host}\``) : true;
|
|
||||||
},
|
|
||||||
schemaTables () {
|
|
||||||
const schemaTables = this.workspace.structure
|
|
||||||
.filter(schema => schema.name === this.schema)
|
|
||||||
.map(schema => schema.tables);
|
.map(schema => schema.tables);
|
||||||
|
|
||||||
return schemaTables.length ? schemaTables[0].filter(table => table.type === 'table') : [];
|
return schemaTables.length ? schemaTables[0].filter(table => table.type === 'table') : [];
|
||||||
},
|
});
|
||||||
users () {
|
|
||||||
const users = [{ value: '' }, ...this.workspace.users];
|
const users = computed(() => {
|
||||||
if (!this.isDefinerInUsers) {
|
const users = [{ value: '' }, ...workspace.value.users];
|
||||||
const [name, host] = this.originalTrigger.definer.replaceAll('`', '').split('@');
|
if (!isDefinerInUsers.value) {
|
||||||
|
const [name, host] = originalTrigger.value.definer.replaceAll('`', '').split('@');
|
||||||
users.unshift({ name, host });
|
users.unshift({ name, host });
|
||||||
}
|
}
|
||||||
|
|
||||||
return users;
|
return users;
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
async schema () {
|
|
||||||
if (this.isSelected) {
|
|
||||||
await this.getTriggerData();
|
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localTrigger.sql);
|
|
||||||
this.lastTrigger = this.trigger;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async trigger () {
|
|
||||||
if (this.isSelected) {
|
|
||||||
await this.getTriggerData();
|
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localTrigger.sql);
|
|
||||||
this.lastTrigger = this.trigger;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async isSelected (val) {
|
|
||||||
if (val) {
|
|
||||||
this.changeBreadcrumbs({ schema: this.schema, trigger: this.trigger });
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
this.resizeQueryEditor();
|
|
||||||
}, 200);
|
|
||||||
|
|
||||||
if (this.lastTrigger !== this.trigger)
|
|
||||||
this.getTriggerData();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
isChanged (val) {
|
|
||||||
this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async created () {
|
|
||||||
await this.getTriggerData();
|
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localTrigger.sql);
|
|
||||||
window.addEventListener('keydown', this.onKey);
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
window.addEventListener('resize', this.resizeQueryEditor);
|
|
||||||
},
|
|
||||||
unmounted () {
|
|
||||||
window.removeEventListener('resize', this.resizeQueryEditor);
|
|
||||||
},
|
|
||||||
beforeUnmount () {
|
|
||||||
window.removeEventListener('keydown', this.onKey);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async getTriggerData () {
|
|
||||||
if (!this.trigger) return;
|
|
||||||
|
|
||||||
Object.keys(this.localEvents).forEach(event => {
|
|
||||||
this.localEvents[event] = false;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.localTrigger = { sql: '' };
|
const getTriggerData = async () => {
|
||||||
this.isLoading = true;
|
if (!props.trigger) return;
|
||||||
this.lastTrigger = this.trigger;
|
|
||||||
|
Object.keys(localEvents.value).forEach((event: TriggerEventName) => {
|
||||||
|
localEvents.value[event] = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
localTrigger.value = { sql: '' };
|
||||||
|
isLoading.value = true;
|
||||||
|
lastTrigger.value = props.trigger;
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: props.connection.uid,
|
||||||
schema: this.schema,
|
schema: props.schema,
|
||||||
trigger: this.trigger
|
trigger: props.trigger
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { status, response } = await Triggers.getTriggerInformations(params);
|
const { status, response } = await Triggers.getTriggerInformations(params);
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
this.originalTrigger = response;
|
originalTrigger.value = response;
|
||||||
this.localTrigger = JSON.parse(JSON.stringify(this.originalTrigger));
|
localTrigger.value = JSON.parse(JSON.stringify(originalTrigger.value));
|
||||||
this.sqlProxy = this.localTrigger.sql;
|
|
||||||
|
|
||||||
if (this.customizations.triggerMultipleEvents) {
|
if (customizations.value.triggerMultipleEvents) {
|
||||||
this.originalTrigger.event.forEach(e => {
|
originalTrigger.value.event.forEach((event: TriggerEventName) => {
|
||||||
this.localEvents[e] = true;
|
localEvents.value[event] = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
addNotification({ status: 'error', message: response });
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.resizeQueryEditor();
|
resizeQueryEditor();
|
||||||
this.isLoading = false;
|
isLoading.value = false;
|
||||||
},
|
};
|
||||||
changeEvents (event) {
|
|
||||||
if (this.customizations.triggerMultipleEvents) {
|
const changeEvents = (event: TriggerEventName) => {
|
||||||
this.localEvents[event] = !this.localEvents[event];
|
if (customizations.value.triggerMultipleEvents) {
|
||||||
this.localTrigger.event = [];
|
localEvents.value[event] = !localEvents.value[event];
|
||||||
for (const key in this.localEvents) {
|
localTrigger.value.event = [];
|
||||||
if (this.localEvents[key])
|
for (const key in localEvents.value) {
|
||||||
this.localTrigger.event.push(key);
|
if (localEvents.value[key as TriggerEventName])
|
||||||
|
localTrigger.value.event.push(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
async saveChanges () {
|
|
||||||
if (this.isSaving) return;
|
const saveChanges = async () => {
|
||||||
this.isSaving = true;
|
if (isSaving.value) return;
|
||||||
|
isSaving.value = true;
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: props.connection.uid,
|
||||||
trigger: {
|
trigger: {
|
||||||
...this.localTrigger,
|
...localTrigger.value,
|
||||||
schema: this.schema,
|
schema: props.schema,
|
||||||
oldName: this.originalTrigger.name
|
oldName: originalTrigger.value.name
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -317,65 +258,108 @@ export default {
|
|||||||
const { status, response } = await Triggers.alterTrigger(params);
|
const { status, response } = await Triggers.alterTrigger(params);
|
||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
await this.refreshStructure(this.connection.uid);
|
await refreshStructure(props.connection.uid);
|
||||||
|
|
||||||
if (this.originalTrigger.name !== this.localTrigger.name) {
|
if (originalTrigger.value.name !== localTrigger.value.name) {
|
||||||
const triggerName = this.customizations.triggerTableInName ? `${this.localTrigger.table}.${this.localTrigger.name}` : this.localTrigger.name;
|
const triggerName = customizations.value.triggerTableInName ? `${localTrigger.value.table}.${localTrigger.value.name}` : localTrigger.value.name;
|
||||||
const triggerOldName = this.customizations.triggerTableInName ? `${this.originalTrigger.table}.${this.originalTrigger.name}` : this.originalTrigger.name;
|
const triggerOldName = customizations.value.triggerTableInName ? `${originalTrigger.value.table}.${originalTrigger.value.name}` : originalTrigger.value.name;
|
||||||
|
|
||||||
this.renameTabs({
|
renameTabs({
|
||||||
uid: this.connection.uid,
|
uid: props.connection.uid,
|
||||||
schema: this.schema,
|
schema: props.schema,
|
||||||
elementName: triggerOldName,
|
elementName: triggerOldName,
|
||||||
elementNewName: triggerName,
|
elementNewName: triggerName,
|
||||||
elementType: 'trigger'
|
elementType: 'trigger'
|
||||||
});
|
});
|
||||||
|
|
||||||
this.changeBreadcrumbs({ schema: this.schema, trigger: triggerName });
|
changeBreadcrumbs({ schema: props.schema, trigger: triggerName });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.getTriggerData();
|
getTriggerData();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
addNotification({ status: 'error', message: response });
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isSaving = false;
|
isSaving.value = false;
|
||||||
},
|
};
|
||||||
clearChanges () {
|
|
||||||
this.localTrigger = JSON.parse(JSON.stringify(this.originalTrigger));
|
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localTrigger.sql);
|
|
||||||
|
|
||||||
Object.keys(this.localEvents).forEach(event => {
|
const clearChanges = () => {
|
||||||
this.localEvents[event] = false;
|
localTrigger.value = JSON.parse(JSON.stringify(originalTrigger.value));
|
||||||
|
queryEditor.value.editor.session.setValue(localTrigger.value.sql);
|
||||||
|
|
||||||
|
Object.keys(localEvents.value).forEach((event: TriggerEventName) => {
|
||||||
|
localEvents.value[event] = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.customizations.triggerMultipleEvents) {
|
if (customizations.value.triggerMultipleEvents) {
|
||||||
this.originalTrigger.event.forEach(e => {
|
originalTrigger.value.event.forEach((event: TriggerEventName) => {
|
||||||
this.localEvents[e] = true;
|
localEvents.value[event] = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
resizeQueryEditor () {
|
|
||||||
if (this.$refs.queryEditor) {
|
const resizeQueryEditor = () => {
|
||||||
|
if (queryEditor.value) {
|
||||||
const footer = document.getElementById('footer');
|
const footer = document.getElementById('footer');
|
||||||
const size = window.innerHeight - this.$refs.queryEditor.$el.getBoundingClientRect().top - footer.offsetHeight;
|
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - footer.offsetHeight;
|
||||||
this.editorHeight = size;
|
editorHeight.value = size;
|
||||||
this.$refs.queryEditor.editor.resize();
|
queryEditor.value.editor.resize();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
onKey (e) {
|
|
||||||
if (this.isSelected) {
|
const onKey = (e: KeyboardEvent) => {
|
||||||
|
if (props.isSelected) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (e.ctrlKey && e.keyCode === 83) { // CTRL + S
|
if (e.ctrlKey && e.key === 's') { // CTRL + S
|
||||||
if (this.isChanged)
|
if (isChanged.value)
|
||||||
this.saveChanges();
|
saveChanges();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(() => props.schema, async () => {
|
||||||
|
if (props.isSelected) {
|
||||||
|
await getTriggerData();
|
||||||
|
queryEditor.value.editor.session.setValue(localTrigger.value.sql);
|
||||||
|
lastTrigger.value = props.trigger;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => props.trigger, async () => {
|
||||||
|
if (props.isSelected) {
|
||||||
|
await getTriggerData();
|
||||||
|
queryEditor.value.editor.session.setValue(localTrigger.value.sql);
|
||||||
|
lastTrigger.value = props.trigger;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => props.isSelected, (val) => {
|
||||||
|
if (val) changeBreadcrumbs({ schema: props.schema });
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(isChanged, (val) => {
|
||||||
|
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: val });
|
||||||
|
});
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
await getTriggerData();
|
||||||
|
queryEditor.value.editor.session.setValue(localTrigger.value.sql);
|
||||||
|
window.addEventListener('keydown', onKey);
|
||||||
|
})();
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('resize', resizeQueryEditor);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('resize', resizeQueryEditor);
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.removeEventListener('keydown', onKey);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -11,16 +11,16 @@
|
|||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
<span>{{ $t('word.save') }}</span>
|
<span>{{ t('word.save') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
class="btn btn-link btn-sm mr-0"
|
class="btn btn-link btn-sm mr-0"
|
||||||
:title="$t('message.clearChanges')"
|
:title="t('message.clearChanges')"
|
||||||
@click="clearChanges"
|
@click="clearChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
||||||
<span>{{ $t('word.clear') }}</span>
|
<span>{{ t('word.clear') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -30,7 +30,7 @@
|
|||||||
<div v-if="customizations.triggerFunctionlanguages" class="column col-auto">
|
<div v-if="customizations.triggerFunctionlanguages" class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('word.language') }}
|
{{ t('word.language') }}
|
||||||
</label>
|
</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localFunction.language"
|
v-model="localFunction.language"
|
||||||
@ -42,20 +42,20 @@
|
|||||||
<div v-if="customizations.definer" class="column col-auto">
|
<div v-if="customizations.definer" class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('word.definer') }}
|
{{ t('word.definer') }}
|
||||||
</label>
|
</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localFunction.definer"
|
v-model="localFunction.definer"
|
||||||
:options="workspace.users"
|
:options="workspace.users"
|
||||||
:option-label="(user) => user.value === '' ? $t('message.currentUser') : `${user.name}@${user.host}`"
|
:option-label="(user: any) => user.value === '' ? t('message.currentUser') : `${user.name}@${user.host}`"
|
||||||
:option-track-by="(user) => user.value === '' ? '' : `\`${user.name}\`@\`${user.host}\``"
|
:option-track-by="(user: any) => user.value === '' ? '' : `\`${user.name}\`@\`${user.host}\``"
|
||||||
class="form-select"
|
class="form-select"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="customizations.comment" class="form-group">
|
<div v-if="customizations.comment" class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('word.comment') }}
|
{{ t('word.comment') }}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
v-model="localFunction.comment"
|
v-model="localFunction.comment"
|
||||||
@ -67,7 +67,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
||||||
<BaseLoader v-if="isLoading" />
|
<BaseLoader v-if="isLoading" />
|
||||||
<label class="form-label ml-2">{{ $t('message.functionBody') }}</label>
|
<label class="form-label ml-2">{{ t('message.functionBody') }}</label>
|
||||||
<QueryEditor
|
<QueryEditor
|
||||||
v-show="isSelected"
|
v-show="isSelected"
|
||||||
ref="queryEditor"
|
ref="queryEditor"
|
||||||
@ -77,295 +77,194 @@
|
|||||||
:height="editorHeight"
|
:height="editorHeight"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<ModalAskParameters
|
|
||||||
v-if="isAskingParameters"
|
|
||||||
:local-routine="localFunction"
|
|
||||||
:client="workspace.client"
|
|
||||||
@confirm="runFunction"
|
|
||||||
@close="hideAskParamsModal"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import { storeToRefs } from 'pinia';
|
import { Component, computed, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch } from 'vue';
|
||||||
|
import { Ace } from 'ace-builds';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
import { useNotificationsStore } from '@/stores/notifications';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
import { uidGen } from 'common/libs/uidGen';
|
import BaseLoader from '@/components/BaseLoader.vue';
|
||||||
import BaseLoader from '@/components/BaseLoader';
|
import QueryEditor from '@/components/QueryEditor.vue';
|
||||||
import QueryEditor from '@/components/QueryEditor';
|
|
||||||
import ModalAskParameters from '@/components/ModalAskParameters';
|
|
||||||
import Functions from '@/ipc-api/Functions';
|
import Functions from '@/ipc-api/Functions';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
import { AlterFunctionParams, TriggerFunctionInfos } from 'common/interfaces/antares';
|
||||||
|
|
||||||
export default {
|
const { t } = useI18n();
|
||||||
name: 'WorkspaceTabPropsTriggerFunction',
|
|
||||||
components: {
|
const props = defineProps({
|
||||||
BaseLoader,
|
|
||||||
QueryEditor,
|
|
||||||
ModalAskParameters,
|
|
||||||
BaseSelect
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
tabUid: String,
|
tabUid: String,
|
||||||
connection: Object,
|
connection: Object,
|
||||||
function: String,
|
function: String,
|
||||||
isSelected: Boolean,
|
isSelected: Boolean,
|
||||||
schema: String
|
schema: String
|
||||||
},
|
});
|
||||||
setup () {
|
|
||||||
const { addNotification } = useNotificationsStore();
|
const { addNotification } = useNotificationsStore();
|
||||||
const workspacesStore = useWorkspacesStore();
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
|
||||||
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getWorkspace,
|
getWorkspace,
|
||||||
refreshStructure,
|
refreshStructure,
|
||||||
renameTabs,
|
renameTabs,
|
||||||
newTab,
|
|
||||||
changeBreadcrumbs,
|
changeBreadcrumbs,
|
||||||
setUnsavedChanges
|
setUnsavedChanges
|
||||||
} = workspacesStore;
|
} = workspacesStore;
|
||||||
|
|
||||||
return {
|
const queryEditor: Ref<Component & {editor: Ace.Editor; $el: HTMLElement}> = ref(null);
|
||||||
addNotification,
|
const isLoading = ref(false);
|
||||||
selectedWorkspace,
|
const isSaving = ref(false);
|
||||||
getWorkspace,
|
const originalFunction: Ref<TriggerFunctionInfos> = ref(null);
|
||||||
refreshStructure,
|
const localFunction: Ref<TriggerFunctionInfos> = ref(null);
|
||||||
renameTabs,
|
const lastFunction = ref(null);
|
||||||
newTab,
|
const sqlProxy = ref('');
|
||||||
changeBreadcrumbs,
|
const editorHeight = ref(300);
|
||||||
setUnsavedChanges
|
|
||||||
};
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
isLoading: false,
|
|
||||||
isSaving: false,
|
|
||||||
isParamsModal: false,
|
|
||||||
isAskingParameters: false,
|
|
||||||
originalFunction: null,
|
|
||||||
localFunction: { sql: '' },
|
|
||||||
lastFunction: null,
|
|
||||||
sqlProxy: '',
|
|
||||||
editorHeight: 300
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
workspace () {
|
|
||||||
return this.getWorkspace(this.connection.uid);
|
|
||||||
},
|
|
||||||
customizations () {
|
|
||||||
return this.workspace.customizations;
|
|
||||||
},
|
|
||||||
isChanged () {
|
|
||||||
return JSON.stringify(this.originalFunction) !== JSON.stringify(this.localFunction);
|
|
||||||
},
|
|
||||||
isDefinerInUsers () {
|
|
||||||
return this.originalFunction
|
|
||||||
? this.workspace.users.some(user => this.originalFunction.definer === `\`${user.name}\`@\`${user.host}\``)
|
|
||||||
: true;
|
|
||||||
},
|
|
||||||
schemaTables () {
|
|
||||||
const schemaTables = this.workspace.structure
|
|
||||||
.filter(schema => schema.name === this.schema)
|
|
||||||
.map(schema => schema.tables);
|
|
||||||
|
|
||||||
return schemaTables.length ? schemaTables[0].filter(table => table.type === 'table') : [];
|
const workspace = computed(() => getWorkspace(props.connection.uid));
|
||||||
}
|
const customizations = computed(() => workspace.value.customizations);
|
||||||
},
|
const isChanged = computed(() => JSON.stringify(originalFunction.value) !== JSON.stringify(localFunction.value));
|
||||||
watch: {
|
|
||||||
async schema () {
|
|
||||||
if (this.isSelected) {
|
|
||||||
await this.getFunctionData();
|
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
|
|
||||||
this.lastFunction = this.function;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async function () {
|
|
||||||
if (this.isSelected) {
|
|
||||||
await this.getFunctionData();
|
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
|
|
||||||
this.lastFunction = this.function;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async isSelected (val) {
|
|
||||||
if (val) {
|
|
||||||
this.changeBreadcrumbs({ schema: this.schema, triggerFunction: this.function });
|
|
||||||
|
|
||||||
setTimeout(() => {
|
const getFunctionData = async () => {
|
||||||
this.resizeQueryEditor();
|
if (!props.function) return;
|
||||||
}, 200);
|
|
||||||
|
|
||||||
if (this.lastFunction !== this.function)
|
isLoading.value = true;
|
||||||
await this.getFunctionData();
|
localFunction.value = { name: '', sql: '', type: '', definer: null };
|
||||||
}
|
lastFunction.value = props.function;
|
||||||
},
|
|
||||||
isChanged (val) {
|
|
||||||
this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async created () {
|
|
||||||
await this.getFunctionData();
|
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
|
|
||||||
window.addEventListener('keydown', this.onKey);
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
window.addEventListener('resize', this.resizeQueryEditor);
|
|
||||||
},
|
|
||||||
unmounted () {
|
|
||||||
window.removeEventListener('resize', this.resizeQueryEditor);
|
|
||||||
},
|
|
||||||
beforeUnmount () {
|
|
||||||
window.removeEventListener('keydown', this.onKey);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async getFunctionData () {
|
|
||||||
if (!this.function) return;
|
|
||||||
|
|
||||||
this.isLoading = true;
|
|
||||||
this.localFunction = { sql: '' };
|
|
||||||
this.lastFunction = this.function;
|
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: props.connection.uid,
|
||||||
schema: this.schema,
|
schema: props.schema,
|
||||||
func: this.function
|
func: props.function
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { status, response } = await Functions.getFunctionInformations(params);
|
const { status, response } = await Functions.getFunctionInformations(params);
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
this.originalFunction = response;
|
originalFunction.value = response;
|
||||||
|
|
||||||
this.originalFunction.parameters = [...this.originalFunction.parameters.map(param => {
|
localFunction.value = JSON.parse(JSON.stringify(originalFunction.value));
|
||||||
param._antares_id = uidGen();
|
sqlProxy.value = localFunction.value.sql;
|
||||||
return param;
|
|
||||||
})];
|
|
||||||
|
|
||||||
this.localFunction = JSON.parse(JSON.stringify(this.originalFunction));
|
|
||||||
this.sqlProxy = this.localFunction.sql;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
addNotification({ status: 'error', message: response });
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.resizeQueryEditor();
|
resizeQueryEditor();
|
||||||
this.isLoading = false;
|
isLoading.value = false;
|
||||||
},
|
};
|
||||||
async saveChanges () {
|
|
||||||
if (this.isSaving) return;
|
const saveChanges = async () => {
|
||||||
this.isSaving = true;
|
if (isSaving.value) return;
|
||||||
|
isSaving.value = true;
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: props.connection.uid,
|
||||||
func: {
|
func: {
|
||||||
...this.localFunction,
|
...localFunction.value,
|
||||||
schema: this.schema,
|
schema: props.schema,
|
||||||
oldName: this.originalFunction.name
|
oldName: originalFunction.value.name
|
||||||
}
|
} as AlterFunctionParams
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { status, response } = await Functions.alterTriggerFunction(params);
|
const { status, response } = await Functions.alterTriggerFunction(params);
|
||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
const oldName = this.originalFunction.name;
|
const oldName = originalFunction.value.name;
|
||||||
|
|
||||||
await this.refreshStructure(this.connection.uid);
|
await refreshStructure(props.connection.uid);
|
||||||
|
|
||||||
if (oldName !== this.localFunction.name) {
|
if (oldName !== localFunction.value.name) {
|
||||||
this.renameTabs({
|
renameTabs({
|
||||||
uid: this.connection.uid,
|
uid: props.connection.uid,
|
||||||
schema: this.schema,
|
schema: props.schema,
|
||||||
elementName: oldName,
|
elementName: oldName,
|
||||||
elementNewName: this.localFunction.name,
|
elementNewName: localFunction.value.name,
|
||||||
elementType: 'triggerFunction'
|
elementType: 'triggerFunction'
|
||||||
});
|
});
|
||||||
|
|
||||||
this.changeBreadcrumbs({ schema: this.schema, triggerFunction: this.localFunction.name });
|
changeBreadcrumbs({ schema: props.schema, triggerFunction: localFunction.value.name });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.getFunctionData();
|
getFunctionData();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
addNotification({ status: 'error', message: response });
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isSaving = false;
|
isSaving.value = false;
|
||||||
},
|
};
|
||||||
clearChanges () {
|
|
||||||
this.localFunction = JSON.parse(JSON.stringify(this.originalFunction));
|
const clearChanges = () => {
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
|
localFunction.value = JSON.parse(JSON.stringify(originalFunction.value));
|
||||||
},
|
queryEditor.value.editor.session.setValue(localFunction.value.sql);
|
||||||
resizeQueryEditor () {
|
};
|
||||||
if (this.$refs.queryEditor) {
|
|
||||||
|
const resizeQueryEditor = () => {
|
||||||
|
if (queryEditor.value) {
|
||||||
const footer = document.getElementById('footer');
|
const footer = document.getElementById('footer');
|
||||||
const size = window.innerHeight - this.$refs.queryEditor.$el.getBoundingClientRect().top - footer.offsetHeight;
|
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - footer.offsetHeight;
|
||||||
this.editorHeight = size;
|
editorHeight.value = size;
|
||||||
this.$refs.queryEditor.editor.resize();
|
queryEditor.value.editor.resize();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
optionsUpdate (options) {
|
|
||||||
this.localFunction = options;
|
|
||||||
},
|
|
||||||
parametersUpdate (parameters) {
|
|
||||||
this.localFunction = { ...this.localFunction, parameters };
|
|
||||||
},
|
|
||||||
runFunctionCheck () {
|
|
||||||
if (this.localFunction.parameters.length)
|
|
||||||
this.showAskParamsModal();
|
|
||||||
else
|
|
||||||
this.runFunction();
|
|
||||||
},
|
|
||||||
runFunction (params) {
|
|
||||||
if (!params) params = [];
|
|
||||||
|
|
||||||
let sql;
|
const onKey = (e: KeyboardEvent) => {
|
||||||
switch (this.connection.client) { // TODO: move in a better place
|
if (props.isSelected) {
|
||||||
case 'maria':
|
|
||||||
case 'mysql':
|
|
||||||
sql = `SELECT \`${this.originalFunction.name}\` (${params.join(',')})`;
|
|
||||||
break;
|
|
||||||
case 'pg':
|
|
||||||
sql = `SELECT ${this.originalFunction.name}(${params.join(',')})`;
|
|
||||||
break;
|
|
||||||
case 'mssql':
|
|
||||||
sql = `SELECT ${this.originalFunction.name} ${params.join(',')}`;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
sql = `SELECT \`${this.originalFunction.name}\` (${params.join(',')})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.newTab({ uid: this.connection.uid, content: sql, type: 'query', autorun: true });
|
|
||||||
},
|
|
||||||
showParamsModal () {
|
|
||||||
this.isParamsModal = true;
|
|
||||||
},
|
|
||||||
hideParamsModal () {
|
|
||||||
this.isParamsModal = false;
|
|
||||||
},
|
|
||||||
showAskParamsModal () {
|
|
||||||
this.isAskingParameters = true;
|
|
||||||
},
|
|
||||||
hideAskParamsModal () {
|
|
||||||
this.isAskingParameters = false;
|
|
||||||
},
|
|
||||||
onKey (e) {
|
|
||||||
if (this.isSelected) {
|
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (e.ctrlKey && e.keyCode === 83) { // CTRL + S
|
if (e.ctrlKey && e.key === 's') { // CTRL + S
|
||||||
if (this.isChanged)
|
if (isChanged.value)
|
||||||
this.saveChanges();
|
saveChanges();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(() => props.schema, async () => {
|
||||||
|
if (props.isSelected) {
|
||||||
|
await getFunctionData();
|
||||||
|
queryEditor.value.editor.session.setValue(localFunction.value.sql);
|
||||||
|
lastFunction.value = props.function;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => props.function, async () => {
|
||||||
|
if (props.isSelected) {
|
||||||
|
await getFunctionData();
|
||||||
|
queryEditor.value.editor.session.setValue(localFunction.value.sql);
|
||||||
|
lastFunction.value = props.function;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => props.isSelected, (val) => {
|
||||||
|
if (val) changeBreadcrumbs({ schema: props.schema });
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(isChanged, (val) => {
|
||||||
|
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: val });
|
||||||
|
});
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
await getFunctionData();
|
||||||
|
queryEditor.value.editor.session.setValue(localFunction.value.sql);
|
||||||
|
window.addEventListener('keydown', onKey);
|
||||||
|
})();
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('resize', resizeQueryEditor);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('resize', resizeQueryEditor);
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.removeEventListener('keydown', onKey);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -11,20 +11,20 @@
|
|||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
<span>{{ $t('word.save') }}</span>
|
<span>{{ t('word.save') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
class="btn btn-link btn-sm mr-0"
|
class="btn btn-link btn-sm mr-0"
|
||||||
:title="$t('message.clearChanges')"
|
:title="t('message.clearChanges')"
|
||||||
@click="clearChanges"
|
@click="clearChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
||||||
<span>{{ $t('word.clear') }}</span>
|
<span>{{ t('word.clear') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="workspace-query-info">
|
<div class="workspace-query-info">
|
||||||
<div class="d-flex" :title="$t('word.schema')">
|
<div class="d-flex" :title="t('word.schema')">
|
||||||
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
|
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -34,7 +34,7 @@
|
|||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column col-auto">
|
<div class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">{{ $t('word.name') }}</label>
|
<label class="form-label">{{ t('word.name') }}</label>
|
||||||
<input
|
<input
|
||||||
v-model="localView.name"
|
v-model="localView.name"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
@ -44,19 +44,19 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="column col-auto">
|
<div class="column col-auto">
|
||||||
<div v-if="workspace.customizations.definer" class="form-group">
|
<div v-if="workspace.customizations.definer" class="form-group">
|
||||||
<label class="form-label">{{ $t('word.definer') }}</label>
|
<label class="form-label">{{ t('word.definer') }}</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localView.definer"
|
v-model="localView.definer"
|
||||||
:options="users"
|
:options="users"
|
||||||
:option-label="(user) => user.value === '' ? $t('message.currentUser') : `${user.name}@${user.host}`"
|
:option-label="(user: any) => user.value === '' ? t('message.currentUser') : `${user.name}@${user.host}`"
|
||||||
:option-track-by="(user) => user.value === '' ? '' : `\`${user.name}\`@\`${user.host}\``"
|
:option-track-by="(user: any) => user.value === '' ? '' : `\`${user.name}\`@\`${user.host}\``"
|
||||||
class="form-select"
|
class="form-select"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column col-auto mr-2">
|
<div class="column col-auto mr-2">
|
||||||
<div v-if="workspace.customizations.viewSqlSecurity" class="form-group">
|
<div v-if="workspace.customizations.viewSqlSecurity" class="form-group">
|
||||||
<label class="form-label">{{ $t('message.sqlSecurity') }}</label>
|
<label class="form-label">{{ t('message.sqlSecurity') }}</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localView.security"
|
v-model="localView.security"
|
||||||
:options="['DEFINER', 'INVOKER']"
|
:options="['DEFINER', 'INVOKER']"
|
||||||
@ -66,7 +66,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="column col-auto mr-2">
|
<div class="column col-auto mr-2">
|
||||||
<div v-if="workspace.customizations.viewAlgorithm" class="form-group">
|
<div v-if="workspace.customizations.viewAlgorithm" class="form-group">
|
||||||
<label class="form-label">{{ $t('word.algorithm') }}</label>
|
<label class="form-label">{{ t('word.algorithm') }}</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localView.algorithm"
|
v-model="localView.algorithm"
|
||||||
:options="['UNDEFINED', 'MERGE', 'TEMPTABLE']"
|
:options="['UNDEFINED', 'MERGE', 'TEMPTABLE']"
|
||||||
@ -76,10 +76,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-if="workspace.customizations.viewUpdateOption" class="column col-auto mr-2">
|
<div v-if="workspace.customizations.viewUpdateOption" class="column col-auto mr-2">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">{{ $t('message.updateOption') }}</label>
|
<label class="form-label">{{ t('message.updateOption') }}</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localView.updateOption"
|
v-model="localView.updateOption"
|
||||||
:option-track-by="(user) => user.value"
|
:option-track-by="(user: any) => user.value"
|
||||||
:options="[{label: 'None', value: ''}, {label: 'CASCADED', value: 'CASCADED'}, {label: 'LOCAL', value: 'LOCAL'}]"
|
:options="[{label: 'None', value: ''}, {label: 'CASCADED', value: 'CASCADED'}, {label: 'LOCAL', value: 'LOCAL'}]"
|
||||||
class="form-select"
|
class="form-select"
|
||||||
/>
|
/>
|
||||||
@ -89,7 +89,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
||||||
<BaseLoader v-if="isLoading" />
|
<BaseLoader v-if="isLoading" />
|
||||||
<label class="form-label ml-2">{{ $t('message.selectStatement') }}</label>
|
<label class="form-label ml-2">{{ t('message.selectStatement') }}</label>
|
||||||
<QueryEditor
|
<QueryEditor
|
||||||
v-show="isSelected"
|
v-show="isSelected"
|
||||||
ref="queryEditor"
|
ref="queryEditor"
|
||||||
@ -102,35 +102,30 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import { storeToRefs } from 'pinia';
|
import { Component, computed, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch } from 'vue';
|
||||||
|
import { Ace } from 'ace-builds';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
import { useNotificationsStore } from '@/stores/notifications';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
import BaseLoader from '@/components/BaseLoader';
|
import BaseLoader from '@/components/BaseLoader.vue';
|
||||||
import QueryEditor from '@/components/QueryEditor';
|
import QueryEditor from '@/components/QueryEditor.vue';
|
||||||
import Views from '@/ipc-api/Views';
|
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
import Views from '@/ipc-api/Views';
|
||||||
|
|
||||||
export default {
|
const { t } = useI18n();
|
||||||
name: 'WorkspaceTabPropsView',
|
|
||||||
components: {
|
const props = defineProps({
|
||||||
BaseLoader,
|
|
||||||
QueryEditor,
|
|
||||||
BaseSelect
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
tabUid: String,
|
tabUid: String,
|
||||||
connection: Object,
|
connection: Object,
|
||||||
isSelected: Boolean,
|
isSelected: Boolean,
|
||||||
schema: String,
|
schema: String,
|
||||||
view: String
|
view: String
|
||||||
},
|
});
|
||||||
setup () {
|
|
||||||
const { addNotification } = useNotificationsStore();
|
const { addNotification } = useNotificationsStore();
|
||||||
const workspacesStore = useWorkspacesStore();
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
|
||||||
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getWorkspace,
|
getWorkspace,
|
||||||
refreshStructure,
|
refreshStructure,
|
||||||
@ -139,131 +134,68 @@ export default {
|
|||||||
setUnsavedChanges
|
setUnsavedChanges
|
||||||
} = workspacesStore;
|
} = workspacesStore;
|
||||||
|
|
||||||
return {
|
const queryEditor: Ref<Component & {editor: Ace.Editor; $el: HTMLElement}> = ref(null);
|
||||||
addNotification,
|
const isLoading = ref(false);
|
||||||
selectedWorkspace,
|
const isSaving = ref(false);
|
||||||
getWorkspace,
|
const originalView = ref(null);
|
||||||
refreshStructure,
|
const localView = ref(null);
|
||||||
renameTabs,
|
const editorHeight = ref(300);
|
||||||
changeBreadcrumbs,
|
const lastView = ref(null);
|
||||||
setUnsavedChanges
|
const sqlProxy = ref('');
|
||||||
};
|
|
||||||
},
|
const workspace = computed(() => getWorkspace(props.connection.uid));
|
||||||
data () {
|
const isChanged = computed(() => JSON.stringify(originalView.value) !== JSON.stringify(localView.value));
|
||||||
return {
|
const isDefinerInUsers = computed(() => originalView.value ? workspace.value.users.some(user => originalView.value.definer === `\`${user.name}\`@\`${user.host}\``) : true);
|
||||||
isLoading: false,
|
|
||||||
isSaving: false,
|
const users = computed(() => {
|
||||||
originalView: null,
|
const users = [{ value: '' }, ...workspace.value.users];
|
||||||
localView: { sql: '' },
|
if (!isDefinerInUsers.value) {
|
||||||
lastView: null,
|
const [name, host] = originalView.value.definer.replaceAll('`', '').split('@');
|
||||||
sqlProxy: '',
|
|
||||||
editorHeight: 300
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
workspace () {
|
|
||||||
return this.getWorkspace(this.connection.uid);
|
|
||||||
},
|
|
||||||
isChanged () {
|
|
||||||
return JSON.stringify(this.originalView) !== JSON.stringify(this.localView);
|
|
||||||
},
|
|
||||||
isDefinerInUsers () {
|
|
||||||
return this.originalView ? this.workspace.users.some(user => this.originalView.definer === `\`${user.name}\`@\`${user.host}\``) : true;
|
|
||||||
},
|
|
||||||
users () {
|
|
||||||
const users = [{ value: '' }, ...this.workspace.users];
|
|
||||||
if (!this.isDefinerInUsers) {
|
|
||||||
const [name, host] = this.originalView.definer.replaceAll('`', '').split('@');
|
|
||||||
users.unshift({ name, host });
|
users.unshift({ name, host });
|
||||||
}
|
}
|
||||||
|
|
||||||
return users;
|
return users;
|
||||||
}
|
});
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
async schema () {
|
|
||||||
if (this.isSelected) {
|
|
||||||
await this.getViewData();
|
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localView.sql);
|
|
||||||
this.lastView = this.view;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async view () {
|
|
||||||
if (this.isSelected) {
|
|
||||||
await this.getViewData();
|
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localView.sql);
|
|
||||||
this.lastView = this.view;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
isSelected (val) {
|
|
||||||
if (val) {
|
|
||||||
this.changeBreadcrumbs({ schema: this.schema, view: this.view });
|
|
||||||
|
|
||||||
setTimeout(() => {
|
const getViewData = async () => {
|
||||||
this.resizeQueryEditor();
|
if (!props.view) return;
|
||||||
}, 200);
|
isLoading.value = true;
|
||||||
|
localView.value = { sql: '' };
|
||||||
if (this.lastView !== this.view)
|
lastView.value = props.view;
|
||||||
this.getViewData();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
isChanged (val) {
|
|
||||||
this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async created () {
|
|
||||||
await this.getViewData();
|
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localView.sql);
|
|
||||||
window.addEventListener('keydown', this.onKey);
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
window.addEventListener('resize', this.resizeQueryEditor);
|
|
||||||
},
|
|
||||||
unmounted () {
|
|
||||||
window.removeEventListener('resize', this.resizeQueryEditor);
|
|
||||||
},
|
|
||||||
beforeUnmount () {
|
|
||||||
window.removeEventListener('keydown', this.onKey);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async getViewData () {
|
|
||||||
if (!this.view) return;
|
|
||||||
this.isLoading = true;
|
|
||||||
this.localView = { sql: '' };
|
|
||||||
this.lastView = this.view;
|
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: props.connection.uid,
|
||||||
schema: this.schema,
|
schema: props.schema,
|
||||||
view: this.view
|
view: props.view
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { status, response } = await Views.getViewInformations(params);
|
const { status, response } = await Views.getViewInformations(params);
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
this.originalView = response;
|
originalView.value = response;
|
||||||
this.localView = JSON.parse(JSON.stringify(this.originalView));
|
localView.value = JSON.parse(JSON.stringify(originalView.value));
|
||||||
this.sqlProxy = this.localView.sql;
|
sqlProxy.value = localView.value.sql;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
addNotification({ status: 'error', message: response });
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.resizeQueryEditor();
|
resizeQueryEditor();
|
||||||
this.isLoading = false;
|
isLoading.value = false;
|
||||||
},
|
};
|
||||||
async saveChanges () {
|
|
||||||
if (this.isSaving) return;
|
const saveChanges = async () => {
|
||||||
this.isSaving = true;
|
if (isSaving.value) return;
|
||||||
|
isSaving.value = true;
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: props.connection.uid,
|
||||||
view: {
|
view: {
|
||||||
...this.localView,
|
...localView.value,
|
||||||
schema: this.schema,
|
schema: props.schema,
|
||||||
oldName: this.originalView.name
|
oldName: originalView.value.name
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -271,54 +203,103 @@ export default {
|
|||||||
const { status, response } = await Views.alterView(params);
|
const { status, response } = await Views.alterView(params);
|
||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
const oldName = this.originalView.name;
|
const oldName = originalView.value.name;
|
||||||
|
|
||||||
await this.refreshStructure(this.connection.uid);
|
await refreshStructure(props.connection.uid);
|
||||||
|
|
||||||
if (oldName !== this.localView.name) {
|
if (oldName !== localView.value.name) {
|
||||||
this.renameTabs({
|
renameTabs({
|
||||||
uid: this.connection.uid,
|
uid: props.connection.uid,
|
||||||
schema: this.schema,
|
schema: props.schema,
|
||||||
elementName: oldName,
|
elementName: oldName,
|
||||||
elementNewName: this.localView.name,
|
elementNewName: localView.value.name,
|
||||||
elementType: 'view'
|
elementType: 'view'
|
||||||
});
|
});
|
||||||
|
|
||||||
this.changeBreadcrumbs({ schema: this.schema, view: this.localView.name });
|
changeBreadcrumbs({ schema: props.schema, view: localView.value.name });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.getViewData();
|
getViewData();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
addNotification({ status: 'error', message: response });
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isSaving = false;
|
isSaving.value = false;
|
||||||
},
|
};
|
||||||
clearChanges () {
|
|
||||||
this.localView = JSON.parse(JSON.stringify(this.originalView));
|
const clearChanges = () => {
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localView.sql);
|
localView.value = JSON.parse(JSON.stringify(originalView.value));
|
||||||
},
|
queryEditor.value.editor.session.setValue(localView.value.sql);
|
||||||
resizeQueryEditor () {
|
};
|
||||||
if (this.$refs.queryEditor) {
|
|
||||||
|
const resizeQueryEditor = () => {
|
||||||
|
if (queryEditor.value) {
|
||||||
const footer = document.getElementById('footer');
|
const footer = document.getElementById('footer');
|
||||||
const size = window.innerHeight - this.$refs.queryEditor.$el.getBoundingClientRect().top - footer.offsetHeight;
|
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - footer.offsetHeight;
|
||||||
this.editorHeight = size;
|
editorHeight.value = size;
|
||||||
this.$refs.queryEditor.editor.resize();
|
queryEditor.value.editor.resize();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
onKey (e) {
|
|
||||||
if (this.isSelected) {
|
const onKey = (e: KeyboardEvent) => {
|
||||||
|
if (props.isSelected) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (e.ctrlKey && e.keyCode === 83) { // CTRL + S
|
if (e.ctrlKey && e.key === 's') { // CTRL + S
|
||||||
if (this.isChanged)
|
if (isChanged.value)
|
||||||
this.saveChanges();
|
saveChanges();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(() => props.schema, async () => {
|
||||||
|
if (props.isSelected) {
|
||||||
|
await getViewData();
|
||||||
|
queryEditor.value.editor.session.setValue(localView.value.sql);
|
||||||
|
lastView.value = props.view;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => props.view, async () => {
|
||||||
|
if (props.isSelected) {
|
||||||
|
await getViewData();
|
||||||
|
queryEditor.value.editor.session.setValue(localView.value.sql);
|
||||||
|
lastView.value = props.view;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => props.isSelected, (val) => {
|
||||||
|
if (val) {
|
||||||
|
changeBreadcrumbs({ schema: props.schema, view: localView.value.name });
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
resizeQueryEditor();
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(isChanged, (val) => {
|
||||||
|
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: val });
|
||||||
|
});
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
await getViewData();
|
||||||
|
queryEditor.value.editor.session.setValue(localView.value.sql);
|
||||||
|
window.addEventListener('keydown', onKey);
|
||||||
|
})();
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('resize', resizeQueryEditor);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('resize', resizeQueryEditor);
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.removeEventListener('keydown', onKey);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -28,11 +28,11 @@
|
|||||||
v-if="showCancel && isQuering"
|
v-if="showCancel && isQuering"
|
||||||
class="btn btn-primary btn-sm cancellable"
|
class="btn btn-primary btn-sm cancellable"
|
||||||
:disabled="!query"
|
:disabled="!query"
|
||||||
:title="$t('word.cancel')"
|
:title="t('word.cancel')"
|
||||||
@click="killTabQuery()"
|
@click="killTabQuery()"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-window-close" />
|
<i class="mdi mdi-24px mdi-window-close" />
|
||||||
<span class="d-invisible pr-1">{{ $t('word.run') }}</span>
|
<span class="d-invisible pr-1">{{ t('word.run') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-else
|
v-else
|
||||||
@ -43,7 +43,7 @@
|
|||||||
@click="runQuery(query)"
|
@click="runQuery(query)"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-play pr-1" />
|
<i class="mdi mdi-24px mdi-play pr-1" />
|
||||||
<span>{{ $t('word.run') }}</span>
|
<span>{{ t('word.run') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
@ -53,7 +53,7 @@
|
|||||||
@click="commitTab()"
|
@click="commitTab()"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-cube-send pr-1" />
|
<i class="mdi mdi-24px mdi-cube-send pr-1" />
|
||||||
<span>{{ $t('word.commit') }}</span>
|
<span>{{ t('word.commit') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="!autocommit"
|
v-if="!autocommit"
|
||||||
@ -62,7 +62,7 @@
|
|||||||
@click="rollbackTab()"
|
@click="rollbackTab()"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-undo-variant pr-1" />
|
<i class="mdi mdi-24px mdi-undo-variant pr-1" />
|
||||||
<span>{{ $t('word.rollback') }}</span>
|
<span>{{ t('word.rollback') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-sm mr-0"
|
class="btn btn-link btn-sm mr-0"
|
||||||
@ -71,7 +71,7 @@
|
|||||||
@click="clear()"
|
@click="clear()"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-delete-sweep pr-1" />
|
<i class="mdi mdi-24px mdi-delete-sweep pr-1" />
|
||||||
<span>{{ $t('word.clear') }}</span>
|
<span>{{ t('word.clear') }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="divider-vert py-3" />
|
<div class="divider-vert py-3" />
|
||||||
@ -83,7 +83,7 @@
|
|||||||
@click="beautify()"
|
@click="beautify()"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-brush pr-1" />
|
<i class="mdi mdi-24px mdi-brush pr-1" />
|
||||||
<span>{{ $t('word.format') }}</span>
|
<span>{{ t('word.format') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-dark btn-sm"
|
class="btn btn-dark btn-sm"
|
||||||
@ -92,7 +92,7 @@
|
|||||||
@click="openHistoryModal()"
|
@click="openHistoryModal()"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-history pr-1" />
|
<i class="mdi mdi-24px mdi-history pr-1" />
|
||||||
<span>{{ $t('word.history') }}</span>
|
<span>{{ t('word.history') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<div class="dropdown table-dropdown pr-2">
|
<div class="dropdown table-dropdown pr-2">
|
||||||
<button
|
<button
|
||||||
@ -101,7 +101,7 @@
|
|||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-file-export mr-1" />
|
<i class="mdi mdi-24px mdi-file-export mr-1" />
|
||||||
<span>{{ $t('word.export') }}</span>
|
<span>{{ t('word.export') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-menu-down" />
|
<i class="mdi mdi-24px mdi-menu-down" />
|
||||||
</button>
|
</button>
|
||||||
<ul class="menu text-left">
|
<ul class="menu text-left">
|
||||||
@ -113,13 +113,13 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-group pr-2" :title="$t('message.commitMode')">
|
<div class="input-group pr-2" :title="t('message.commitMode')">
|
||||||
<i class="input-group-addon addon-sm mdi mdi-24px mdi-source-commit p-0" />
|
<i class="input-group-addon addon-sm mdi mdi-24px mdi-source-commit p-0" />
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="autocommit"
|
v-model="autocommit"
|
||||||
:options="[{value: true, label: $t('message.autoCommit')}, {value: false, label: $t('message.manualCommit')}]"
|
:options="[{value: true, label: t('message.autoCommit')}, {value: false, label: t('message.manualCommit')}]"
|
||||||
:option-label="opt => opt.label"
|
:option-label="(opt: any) => opt.label"
|
||||||
:option-track-by="opt => opt.value"
|
:option-track-by="(opt: any) => opt.value"
|
||||||
class="form-select select-sm text-bold"
|
class="form-select select-sm text-bold"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -128,30 +128,30 @@
|
|||||||
<div
|
<div
|
||||||
v-if="results.length"
|
v-if="results.length"
|
||||||
class="d-flex"
|
class="d-flex"
|
||||||
:title="$t('message.queryDuration')"
|
:title="t('message.queryDuration')"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-timer-sand mdi-rotate-180 pr-1" /> <b>{{ durationsCount / 1000 }}s</b>
|
<i class="mdi mdi-timer-sand mdi-rotate-180 pr-1" /> <b>{{ durationsCount / 1000 }}s</b>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="resultsCount"
|
v-if="resultsCount"
|
||||||
class="d-flex"
|
class="d-flex"
|
||||||
:title="$t('word.results')"
|
:title="t('word.results')"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-equal pr-1" /> <b>{{ resultsCount.toLocaleString() }}</b>
|
<i class="mdi mdi-equal pr-1" /> <b>{{ resultsCount.toLocaleString() }}</b>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="hasAffected"
|
v-if="hasAffected"
|
||||||
class="d-flex"
|
class="d-flex"
|
||||||
:title="$t('message.affectedRows')"
|
:title="t('message.affectedRows')"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-target pr-1" /> <b>{{ affectedCount }}</b>
|
<i class="mdi mdi-target pr-1" /> <b>{{ affectedCount }}</b>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-group" :title="$t('word.schema')">
|
<div class="input-group" :title="t('word.schema')">
|
||||||
<i class="input-group-addon addon-sm mdi mdi-24px mdi-database" />
|
<i class="input-group-addon addon-sm mdi mdi-24px mdi-database" />
|
||||||
|
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="selectedSchema"
|
v-model="selectedSchema"
|
||||||
:options="[{value: null, label: $t('message.noSchema')}, ...databaseSchemas.map(el => ({label: el, value: el}))]"
|
:options="[{value: null, label: t('message.noSchema')}, ...databaseSchemas.map(el => ({label: el, value: el}))]"
|
||||||
class="form-select select-sm text-bold"
|
class="form-select select-sm text-bold"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -183,45 +183,46 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import { storeToRefs } from 'pinia';
|
import { Component, computed, onBeforeUnmount, onMounted, Prop, ref, Ref, watch } from 'vue';
|
||||||
|
import { Ace } from 'ace-builds';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { format } from 'sql-formatter';
|
import { format } from 'sql-formatter';
|
||||||
|
import { ConnectionParams } from 'common/interfaces/antares';
|
||||||
import { useHistoryStore } from '@/stores/history';
|
import { useHistoryStore } from '@/stores/history';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
import { useNotificationsStore } from '@/stores/notifications';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
import Schema from '@/ipc-api/Schema';
|
import Schema from '@/ipc-api/Schema';
|
||||||
import QueryEditor from '@/components/QueryEditor';
|
import QueryEditor from '@/components/QueryEditor.vue';
|
||||||
import BaseLoader from '@/components/BaseLoader';
|
import BaseLoader from '@/components/BaseLoader.vue';
|
||||||
import WorkspaceTabQueryTable from '@/components/WorkspaceTabQueryTable';
|
import WorkspaceTabQueryTable from '@/components/WorkspaceTabQueryTable.vue';
|
||||||
import WorkspaceTabQueryEmptyState from '@/components/WorkspaceTabQueryEmptyState';
|
import WorkspaceTabQueryEmptyState from '@/components/WorkspaceTabQueryEmptyState.vue';
|
||||||
import ModalHistory from '@/components/ModalHistory';
|
import ModalHistory from '@/components/ModalHistory.vue';
|
||||||
import tableTabs from '@/mixins/tableTabs';
|
import { useResultTables } from '@/composables/useResultTables';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
|
||||||
export default {
|
const { t } = useI18n();
|
||||||
name: 'WorkspaceTabQuery',
|
|
||||||
components: {
|
const props = defineProps({
|
||||||
BaseLoader,
|
|
||||||
QueryEditor,
|
|
||||||
WorkspaceTabQueryTable,
|
|
||||||
WorkspaceTabQueryEmptyState,
|
|
||||||
ModalHistory,
|
|
||||||
BaseSelect
|
|
||||||
},
|
|
||||||
mixins: [tableTabs],
|
|
||||||
props: {
|
|
||||||
tabUid: String,
|
tabUid: String,
|
||||||
connection: Object,
|
connection: Object as Prop<ConnectionParams>,
|
||||||
tab: Object,
|
tab: Object,
|
||||||
isSelected: Boolean
|
isSelected: Boolean
|
||||||
},
|
});
|
||||||
setup () {
|
|
||||||
const { getHistoryByWorkspace, saveHistory } = useHistoryStore();
|
const reloadTable = () => runQuery(lastQuery.value);
|
||||||
|
|
||||||
|
const {
|
||||||
|
queryTable,
|
||||||
|
isQuering,
|
||||||
|
updateField,
|
||||||
|
deleteSelected
|
||||||
|
} = useResultTables(props.connection.uid, reloadTable);
|
||||||
|
|
||||||
|
const { saveHistory } = useHistoryStore();
|
||||||
const { addNotification } = useNotificationsStore();
|
const { addNotification } = useNotificationsStore();
|
||||||
const workspacesStore = useWorkspacesStore();
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
|
||||||
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getWorkspace,
|
getWorkspace,
|
||||||
changeBreadcrumbs,
|
changeBreadcrumbs,
|
||||||
@ -229,228 +230,174 @@ export default {
|
|||||||
setUnsavedChanges
|
setUnsavedChanges
|
||||||
} = workspacesStore;
|
} = workspacesStore;
|
||||||
|
|
||||||
return {
|
const queryEditor: Ref<Component & { editor: Ace.Editor; $el: HTMLElement }> = ref(null);
|
||||||
getHistoryByWorkspace,
|
const queryAreaFooter: Ref<HTMLDivElement> = ref(null);
|
||||||
saveHistory,
|
const resizer: Ref<HTMLDivElement> = ref(null);
|
||||||
addNotification,
|
const query = ref('');
|
||||||
selectedWorkspace,
|
const lastQuery = ref('');
|
||||||
getWorkspace,
|
const isCancelling = ref(false);
|
||||||
changeBreadcrumbs,
|
const showCancel = ref(false);
|
||||||
updateTabContent,
|
const autocommit = ref(true);
|
||||||
setUnsavedChanges
|
const results = ref([]);
|
||||||
};
|
const selectedSchema = ref(null);
|
||||||
},
|
const resultsCount = ref(0);
|
||||||
data () {
|
const durationsCount = ref(0);
|
||||||
return {
|
const affectedCount = ref(null);
|
||||||
query: '',
|
const editorHeight = ref(200);
|
||||||
lastQuery: '',
|
const isHistoryOpen = ref(false);
|
||||||
isQuering: false,
|
const debounceTimeout = ref(null);
|
||||||
isCancelling: false,
|
|
||||||
showCancel: false,
|
const workspace = computed(() => getWorkspace(props.connection.uid));
|
||||||
autocommit: true,
|
const breadcrumbsSchema = computed(() => workspace.value.breadcrumbs.schema || null);
|
||||||
results: [],
|
const databaseSchemas = computed(() => {
|
||||||
selectedSchema: null,
|
return workspace.value.structure.reduce((acc, curr) => {
|
||||||
resultsCount: 0,
|
|
||||||
durationsCount: 0,
|
|
||||||
affectedCount: null,
|
|
||||||
editorHeight: 200,
|
|
||||||
isHistoryOpen: false,
|
|
||||||
debounceTimeout: null
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
workspace () {
|
|
||||||
return this.getWorkspace(this.connection.uid);
|
|
||||||
},
|
|
||||||
breadcrumbsSchema () {
|
|
||||||
return this.workspace.breadcrumbs.schema || null;
|
|
||||||
},
|
|
||||||
databaseSchemas () {
|
|
||||||
return this.workspace.structure.reduce((acc, curr) => {
|
|
||||||
acc.push(curr.name);
|
acc.push(curr.name);
|
||||||
return acc;
|
return acc;
|
||||||
}, []);
|
}, []);
|
||||||
},
|
});
|
||||||
isWorkspaceSelected () {
|
const hasResults = computed(() => results.value.length && results.value[0].rows);
|
||||||
return this.workspace.uid === this.selectedWorkspace;
|
const hasAffected = computed(() => affectedCount.value || (!resultsCount.value && affectedCount.value !== null));
|
||||||
},
|
|
||||||
history () {
|
|
||||||
return this.getHistoryByWorkspace(this.connection.uid) || [];
|
|
||||||
},
|
|
||||||
hasResults () {
|
|
||||||
return this.results.length && this.results[0].rows;
|
|
||||||
},
|
|
||||||
hasAffected () {
|
|
||||||
return this.affectedCount || (!this.resultsCount && this.affectedCount !== null);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
query (val) {
|
|
||||||
clearTimeout(this.debounceTimeout);
|
|
||||||
|
|
||||||
this.debounceTimeout = setTimeout(() => {
|
watch(query, (val) => {
|
||||||
this.updateTabContent({
|
clearTimeout(debounceTimeout.value);
|
||||||
uid: this.connection.uid,
|
|
||||||
tab: this.tab.uid,
|
debounceTimeout.value = setTimeout(() => {
|
||||||
|
updateTabContent({
|
||||||
|
uid: props.connection.uid,
|
||||||
|
tab: props.tab.uid,
|
||||||
type: 'query',
|
type: 'query',
|
||||||
schema: this.selectedSchema,
|
schema: selectedSchema.value,
|
||||||
content: val
|
content: val
|
||||||
});
|
});
|
||||||
}, 200);
|
}, 200);
|
||||||
},
|
|
||||||
isSelected (val) {
|
|
||||||
if (val) {
|
|
||||||
this.changeBreadcrumbs({ schema: this.selectedSchema, query: `Query #${this.tab.index}` });
|
|
||||||
setTimeout(() => {
|
|
||||||
if (this.$refs.queryEditor)
|
|
||||||
this.$refs.queryEditor.editor.focus();
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
selectedSchema () {
|
|
||||||
this.changeBreadcrumbs({ schema: this.selectedSchema, query: `Query #${this.tab.index}` });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created () {
|
|
||||||
this.query = this.tab.content;
|
|
||||||
this.selectedSchema = this.tab.schema || this.breadcrumbsSchema;
|
|
||||||
|
|
||||||
if (!this.databaseSchemas.includes(this.selectedSchema))
|
|
||||||
this.selectedSchema = null;
|
|
||||||
|
|
||||||
window.addEventListener('keydown', this.onKey);
|
|
||||||
window.addEventListener('resize', this.onWindowResize);
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
const resizer = this.$refs.resizer;
|
|
||||||
|
|
||||||
resizer.addEventListener('mousedown', e => {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
window.addEventListener('mousemove', this.resize);
|
|
||||||
window.addEventListener('mouseup', this.stopResize);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.tab.autorun)
|
watch(() => props.isSelected, (val) => {
|
||||||
this.runQuery(this.query);
|
if (val) {
|
||||||
},
|
changeBreadcrumbs({ schema: selectedSchema.value, query: `Query #${props.tab.index}` });
|
||||||
beforeUnmount () {
|
setTimeout(() => {
|
||||||
window.removeEventListener('resize', this.onWindowResize);
|
if (queryEditor.value)
|
||||||
window.removeEventListener('keydown', this.onKey);
|
queryEditor.value.editor.focus();
|
||||||
const params = {
|
}, 0);
|
||||||
uid: this.connection.uid,
|
}
|
||||||
tabUid: this.tab.uid
|
});
|
||||||
};
|
|
||||||
Schema.destroyConnectionToCommit(params);
|
watch(selectedSchema, () => {
|
||||||
},
|
changeBreadcrumbs({ schema: selectedSchema.value, query: `Query #${props.tab.index}` });
|
||||||
methods: {
|
});
|
||||||
async runQuery (query) {
|
|
||||||
if (!query || this.isQuering) return;
|
const runQuery = async (query: string) => {
|
||||||
this.isQuering = true;
|
if (!query || isQuering.value) return;
|
||||||
this.clearTabData();
|
isQuering.value = true;
|
||||||
this.$refs.queryTable.resetSort();
|
clearTabData();
|
||||||
|
queryTable.value.resetSort();
|
||||||
|
|
||||||
try { // Query Data
|
try { // Query Data
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: props.connection.uid,
|
||||||
schema: this.selectedSchema,
|
schema: selectedSchema.value,
|
||||||
tabUid: this.tab.uid,
|
tabUid: props.tab.uid,
|
||||||
autocommit: this.autocommit,
|
autocommit: autocommit.value,
|
||||||
query
|
query
|
||||||
};
|
};
|
||||||
|
|
||||||
const { status, response } = await Schema.rawQuery(params);
|
const { status, response } = await Schema.rawQuery(params);
|
||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
this.results = Array.isArray(response) ? response : [response];
|
results.value = Array.isArray(response) ? response : [response];
|
||||||
this.resultsCount = this.results.reduce((acc, curr) => acc + (curr.rows ? curr.rows.length : 0), 0);
|
resultsCount.value = results.value.reduce((acc, curr) => acc + (curr.rows ? curr.rows.length : 0), 0);
|
||||||
this.durationsCount = this.results.reduce((acc, curr) => acc + curr.duration, 0);
|
durationsCount.value = results.value.reduce((acc, curr) => acc + curr.duration, 0);
|
||||||
this.affectedCount = this.results
|
affectedCount.value = results.value
|
||||||
.filter(result => result.report !== null)
|
.filter(result => result.report !== null)
|
||||||
.reduce((acc, curr) => {
|
.reduce((acc, curr) => {
|
||||||
if (acc === null) acc = 0;
|
if (acc === null) acc = 0;
|
||||||
return acc + (curr.report ? curr.report.affectedRows : 0);
|
return acc + (curr.report ? curr.report.affectedRows : 0);
|
||||||
}, null);
|
}, null);
|
||||||
|
|
||||||
this.saveHistory(params);
|
saveHistory(params);
|
||||||
if (!this.autocommit)
|
if (!autocommit.value)
|
||||||
this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: true });
|
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: true });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
addNotification({ status: 'error', message: response });
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isQuering = false;
|
isQuering.value = false;
|
||||||
this.lastQuery = query;
|
lastQuery.value = query;
|
||||||
},
|
};
|
||||||
async killTabQuery () {
|
|
||||||
if (this.isCancelling) return;
|
|
||||||
|
|
||||||
this.isCancelling = true;
|
const killTabQuery = async () => {
|
||||||
|
if (isCancelling.value) return;
|
||||||
|
|
||||||
|
isCancelling.value = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: props.connection.uid,
|
||||||
tabUid: this.tab.uid
|
tabUid: props.tab.uid
|
||||||
};
|
};
|
||||||
|
|
||||||
await Schema.killTabQuery(params);
|
await Schema.killTabQuery(params);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isCancelling = false;
|
isCancelling.value = false;
|
||||||
},
|
};
|
||||||
setCancelButtonVisibility (val) {
|
|
||||||
if (this.workspace.customizations.cancelQueries)
|
const setCancelButtonVisibility = (val: boolean) => {
|
||||||
this.showCancel = val;
|
if (workspace.value.customizations.cancelQueries)
|
||||||
},
|
showCancel.value = val;
|
||||||
reloadTable () {
|
};
|
||||||
this.runQuery(this.lastQuery);
|
|
||||||
},
|
const clearTabData = () => {
|
||||||
clearTabData () {
|
results.value = [];
|
||||||
this.results = [];
|
resultsCount.value = 0;
|
||||||
this.resultsCount = 0;
|
durationsCount.value = 0;
|
||||||
this.durationsCount = 0;
|
affectedCount.value = null;
|
||||||
this.affectedCount = null;
|
};
|
||||||
},
|
|
||||||
resize (e) {
|
const resize = (e: MouseEvent) => {
|
||||||
const el = this.$refs.queryEditor.$el;
|
const el = queryEditor.value.$el;
|
||||||
const queryFooterHeight = this.$refs.queryAreaFooter.clientHeight;
|
const queryFooterHeight = queryAreaFooter.value.clientHeight;
|
||||||
const bottom = e.pageY || this.$refs.resizer.getBoundingClientRect().bottom;
|
const bottom = e.pageY || resizer.value.getBoundingClientRect().bottom;
|
||||||
const maxHeight = window.innerHeight - 100 - queryFooterHeight;
|
const maxHeight = window.innerHeight - 100 - queryFooterHeight;
|
||||||
let editorHeight = bottom - el.getBoundingClientRect().top;
|
let localEditorHeight = bottom - el.getBoundingClientRect().top;
|
||||||
if (editorHeight > maxHeight) editorHeight = maxHeight;
|
if (localEditorHeight > maxHeight) localEditorHeight = maxHeight;
|
||||||
if (editorHeight < 50) editorHeight = 50;
|
if (localEditorHeight < 50) localEditorHeight = 50;
|
||||||
this.editorHeight = editorHeight;
|
editorHeight.value = localEditorHeight;
|
||||||
},
|
};
|
||||||
onWindowResize (e) {
|
|
||||||
const el = this.$refs.queryEditor.$el;
|
const onWindowResize = (e: MouseEvent) => {
|
||||||
const queryFooterHeight = this.$refs.queryAreaFooter.clientHeight;
|
const el = queryEditor.value.$el;
|
||||||
const bottom = e.pageY || this.$refs.resizer.getBoundingClientRect().bottom;
|
const queryFooterHeight = queryAreaFooter.value.clientHeight;
|
||||||
|
const bottom = e.pageY || resizer.value.getBoundingClientRect().bottom;
|
||||||
const maxHeight = window.innerHeight - 100 - queryFooterHeight;
|
const maxHeight = window.innerHeight - 100 - queryFooterHeight;
|
||||||
const editorHeight = bottom - el.getBoundingClientRect().top;
|
const localEditorHeight = bottom - el.getBoundingClientRect().top;
|
||||||
|
|
||||||
if (editorHeight > maxHeight)
|
if (localEditorHeight > maxHeight)
|
||||||
this.editorHeight = maxHeight;
|
editorHeight.value = maxHeight;
|
||||||
},
|
};
|
||||||
stopResize () {
|
|
||||||
window.removeEventListener('mousemove', this.resize);
|
|
||||||
if (this.$refs.queryTable && this.results.length)
|
|
||||||
this.$refs.queryTable.resizeResults();
|
|
||||||
|
|
||||||
if (this.$refs.queryEditor)
|
const stopResize = () => {
|
||||||
this.$refs.queryEditor.editor.resize();
|
window.removeEventListener('mousemove', resize);
|
||||||
},
|
if (queryTable.value && results.value.length)
|
||||||
beautify () {
|
queryTable.value.resizeResults();
|
||||||
if (this.$refs.queryEditor) {
|
|
||||||
let language = 'sql';
|
|
||||||
|
|
||||||
switch (this.workspace.client) {
|
if (queryEditor.value)
|
||||||
|
queryEditor.value.editor.resize();
|
||||||
|
};
|
||||||
|
|
||||||
|
const beautify = () => {
|
||||||
|
if (queryEditor.value) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
let language: any = 'sql';
|
||||||
|
|
||||||
|
switch (workspace.value.client) {
|
||||||
case 'mysql':
|
case 'mysql':
|
||||||
language = 'mysql';
|
language = 'mysql';
|
||||||
break;
|
break;
|
||||||
@ -462,68 +409,105 @@ export default {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const formattedQuery = format(this.query, {
|
const formattedQuery = format(query.value, {
|
||||||
language,
|
language,
|
||||||
uppercase: true
|
uppercase: true
|
||||||
});
|
});
|
||||||
this.$refs.queryEditor.editor.session.setValue(formattedQuery);
|
queryEditor.value.editor.session.setValue(formattedQuery);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
openHistoryModal () {
|
|
||||||
this.isHistoryOpen = true;
|
|
||||||
},
|
|
||||||
selectQuery (sql) {
|
|
||||||
if (this.$refs.queryEditor)
|
|
||||||
this.$refs.queryEditor.editor.session.setValue(sql);
|
|
||||||
|
|
||||||
this.isHistoryOpen = false;
|
const openHistoryModal = () => {
|
||||||
},
|
isHistoryOpen.value = true;
|
||||||
clear () {
|
};
|
||||||
if (this.$refs.queryEditor)
|
|
||||||
this.$refs.queryEditor.editor.session.setValue('');
|
const selectQuery = (sql: string) => {
|
||||||
this.clearTabData();
|
if (queryEditor.value)
|
||||||
},
|
queryEditor.value.editor.session.setValue(sql);
|
||||||
downloadTable (format) {
|
|
||||||
this.$refs.queryTable.downloadTable(format, `${this.tab.type}-${this.tab.index}`);
|
isHistoryOpen.value = false;
|
||||||
},
|
};
|
||||||
async commitTab () {
|
|
||||||
this.isQuering = true;
|
const clear = () => {
|
||||||
|
if (queryEditor.value)
|
||||||
|
queryEditor.value.editor.session.setValue('');
|
||||||
|
clearTabData();
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadTable = (format: 'csv' | 'json') => {
|
||||||
|
queryTable.value.downloadTable(format, `${props.tab.type}-${props.tab.index}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const commitTab = async () => {
|
||||||
|
isQuering.value = true;
|
||||||
try {
|
try {
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: props.connection.uid,
|
||||||
tabUid: this.tab.uid
|
tabUid: props.tab.uid
|
||||||
};
|
};
|
||||||
|
|
||||||
await Schema.commitTab(params);
|
await Schema.commitTab(params);
|
||||||
this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: false });
|
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: false });
|
||||||
this.addNotification({ status: 'success', message: this.$t('message.actionSuccessful', { action: 'COMMIT' }) });
|
addNotification({ status: 'success', message: t('message.actionSuccessful', { action: 'COMMIT' }) });
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isQuering = false;
|
isQuering.value = false;
|
||||||
},
|
};
|
||||||
async rollbackTab () {
|
|
||||||
this.isQuering = true;
|
const rollbackTab = async () => {
|
||||||
|
isQuering.value = true;
|
||||||
try {
|
try {
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: props.connection.uid,
|
||||||
tabUid: this.tab.uid
|
tabUid: props.tab.uid
|
||||||
};
|
};
|
||||||
|
|
||||||
await Schema.rollbackTab(params);
|
await Schema.rollbackTab(params);
|
||||||
this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: false });
|
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: false });
|
||||||
this.addNotification({ status: 'success', message: this.$t('message.actionSuccessful', { action: 'ROLLBACK' }) });
|
addNotification({ status: 'success', message: t('message.actionSuccessful', { action: 'ROLLBACK' }) });
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isQuering = false;
|
isQuering.value = false;
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
query.value = props.tab.content as string;
|
||||||
|
selectedSchema.value = props.tab.schema || breadcrumbsSchema.value;
|
||||||
|
|
||||||
|
if (!databaseSchemas.value.includes(selectedSchema.value))
|
||||||
|
selectedSchema.value = null;
|
||||||
|
|
||||||
|
// window.addEventListener('keydown', onKey);
|
||||||
|
window.addEventListener('resize', onWindowResize);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const localResizer = resizer.value;
|
||||||
|
|
||||||
|
localResizer.addEventListener('mousedown', (e: MouseEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
window.addEventListener('mousemove', resize);
|
||||||
|
window.addEventListener('mouseup', stopResize);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (props.tab.autorun)
|
||||||
|
runQuery(query.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.removeEventListener('resize', onWindowResize);
|
||||||
|
// window.removeEventListener('keydown', onKey);
|
||||||
|
const params = {
|
||||||
|
uid: props.connection.uid,
|
||||||
|
tabUid: props.tab.uid
|
||||||
|
};
|
||||||
|
Schema.destroyConnectionToCommit(params);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
@ -3,25 +3,25 @@
|
|||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column col-16 text-right">
|
<div class="column col-16 text-right">
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
{{ $t('message.runQuery') }}
|
{{ t('message.runQuery') }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="customizations.cancelQueries" class="mb-4">
|
<div v-if="customizations.cancelQueries" class="mb-4">
|
||||||
{{ $t('message.killQuery') }}
|
{{ t('message.killQuery') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
{{ $t('word.format') }}
|
{{ t('word.format') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
{{ $t('word.clear') }}
|
{{ t('word.clear') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
{{ $t('word.history') }}
|
{{ t('word.history') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
{{ $t('message.openNewTab') }}
|
{{ t('message.openNewTab') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
{{ $t('message.closeTab') }}
|
{{ t('message.closeTab') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column col-16">
|
<div class="column col-16">
|
||||||
@ -51,13 +51,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
export default {
|
import { useI18n } from 'vue-i18n';
|
||||||
name: 'WorkspaceTabQueryEmptyState',
|
|
||||||
props: {
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
defineProps({
|
||||||
customizations: Object
|
customizations: Object
|
||||||
}
|
});
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@ -97,131 +97,120 @@
|
|||||||
<template #header>
|
<template #header>
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-delete mr-1" />
|
<i class="mdi mdi-24px mdi-delete mr-1" />
|
||||||
<span class="cut-text">{{ $tc('message.deleteRows', selectedRows.length) }}</span>
|
<span class="cut-text">{{ t('message.deleteRows', selectedRows.length) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #body>
|
<template #body>
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
{{ $tc('message.confirmToDeleteRows', selectedRows.length) }}
|
{{ t('message.confirmToDeleteRows', selectedRows.length) }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</ConfirmModal>
|
</ConfirmModal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
import { Component, computed, nextTick, onMounted, onUnmounted, onUpdated, Prop, ref, Ref, watch } from 'vue';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { uidGen } from 'common/libs/uidGen';
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
|
||||||
import { useSettingsStore } from '@/stores/settings';
|
import { useSettingsStore } from '@/stores/settings';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
import { arrayToFile } from '../libs/arrayToFile';
|
import { arrayToFile } from '../libs/arrayToFile';
|
||||||
import { TEXT, LONG_TEXT, BLOB } from 'common/fieldTypes';
|
import { TEXT, LONG_TEXT, BLOB } from 'common/fieldTypes';
|
||||||
import BaseVirtualScroll from '@/components/BaseVirtualScroll';
|
import BaseVirtualScroll from '@/components/BaseVirtualScroll.vue';
|
||||||
import WorkspaceTabQueryTableRow from '@/components/WorkspaceTabQueryTableRow';
|
import WorkspaceTabQueryTableRow from '@/components/WorkspaceTabQueryTableRow.vue';
|
||||||
import TableContext from '@/components/WorkspaceTabQueryTableContext';
|
import TableContext from '@/components/WorkspaceTabQueryTableContext.vue';
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
import ConfirmModal from '@/components/BaseConfirmModal.vue';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { TableField, QueryResult } from 'common/interfaces/antares';
|
||||||
|
import { TableUpdateParams } from 'common/interfaces/tableApis';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'WorkspaceTabQueryTable',
|
|
||||||
components: {
|
|
||||||
BaseVirtualScroll,
|
|
||||||
WorkspaceTabQueryTableRow,
|
|
||||||
TableContext,
|
|
||||||
ConfirmModal
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
results: Array,
|
|
||||||
connUid: String,
|
|
||||||
mode: String,
|
|
||||||
isSelected: Boolean,
|
|
||||||
elementType: { type: String, default: 'table' }
|
|
||||||
},
|
|
||||||
emits: ['update-field', 'delete-selected', 'hard-sort'],
|
|
||||||
setup () {
|
|
||||||
const { addNotification } = useNotificationsStore();
|
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
const { getWorkspace } = useWorkspacesStore();
|
const { getWorkspace } = useWorkspacesStore();
|
||||||
|
|
||||||
const { dataTabLimit: pageSize } = storeToRefs(settingsStore);
|
const { dataTabLimit: pageSize } = storeToRefs(settingsStore);
|
||||||
|
|
||||||
return {
|
const props = defineProps({
|
||||||
addNotification,
|
results: Array as Prop<QueryResult[]>,
|
||||||
pageSize,
|
connUid: String,
|
||||||
getWorkspace
|
mode: String,
|
||||||
};
|
isSelected: Boolean,
|
||||||
},
|
elementType: { type: String, default: 'table' }
|
||||||
data () {
|
});
|
||||||
return {
|
|
||||||
resultsSize: 0,
|
const emit = defineEmits(['update-field', 'delete-selected', 'hard-sort']);
|
||||||
localResults: [],
|
|
||||||
isContext: false,
|
const resultTable: Ref<Component & {updateWindow: () => void}> = ref(null);
|
||||||
isDeleteConfirmModal: false,
|
const tableWrapper: Ref<HTMLDivElement> = ref(null);
|
||||||
contextEvent: null,
|
const table: Ref<HTMLDivElement> = ref(null);
|
||||||
selectedCell: null,
|
const resultsSize = ref(0);
|
||||||
selectedRows: [],
|
const localResults: Ref<QueryResult<any>[]> = ref([]);
|
||||||
currentSort: '',
|
const isContext = ref(false);
|
||||||
currentSortDir: 'asc',
|
const isDeleteConfirmModal = ref(false);
|
||||||
resultsetIndex: 0,
|
const contextEvent = ref(null);
|
||||||
scrollElement: null,
|
const selectedCell = ref(null);
|
||||||
rowHeight: 23,
|
const selectedRows = ref([]);
|
||||||
selectedField: null,
|
const currentSort = ref('');
|
||||||
isEditingRow: false
|
const currentSortDir = ref('asc');
|
||||||
};
|
const resultsetIndex = ref(0);
|
||||||
},
|
const scrollElement = ref(null);
|
||||||
computed: {
|
const rowHeight = ref(23);
|
||||||
workspaceSchema () {
|
const selectedField = ref(null);
|
||||||
return this.getWorkspace(this.connUid).breadcrumbs.schema;
|
const isEditingRow = ref(false);
|
||||||
},
|
|
||||||
primaryField () {
|
const workspaceSchema = computed(() => getWorkspace(props.connUid).breadcrumbs.schema);
|
||||||
const primaryFields = this.fields.filter(field => field.key === 'pri');
|
|
||||||
const uniqueFields = this.fields.filter(field => field.key === 'uni');
|
const primaryField = computed(() => {
|
||||||
|
const primaryFields = fields.value.filter(field => field.key === 'pri');
|
||||||
|
const uniqueFields = fields.value.filter(field => field.key === 'uni');
|
||||||
|
|
||||||
if ((primaryFields.length > 1 || !primaryFields.length) && (uniqueFields.length > 1 || !uniqueFields.length))
|
if ((primaryFields.length > 1 || !primaryFields.length) && (uniqueFields.length > 1 || !uniqueFields.length))
|
||||||
return false;
|
return null;
|
||||||
|
|
||||||
return primaryFields[0] || uniqueFields[0];
|
return primaryFields[0] || uniqueFields[0];
|
||||||
},
|
});
|
||||||
isSortable () {
|
|
||||||
return this.fields.every(field => field.name);
|
const isSortable = computed(() => {
|
||||||
},
|
return fields.value.every(field => field.name);
|
||||||
isHardSort () {
|
});
|
||||||
return this.mode === 'table' && this.localResults.length === this.pageSize;
|
|
||||||
},
|
const isHardSort = computed(() => {
|
||||||
sortedResults () {
|
return props.mode === 'table' && localResults.value.length === pageSize.value;
|
||||||
if (this.currentSort && !this.isHardSort) {
|
});
|
||||||
return [...this.localResults].sort((a, b) => {
|
|
||||||
|
const sortedResults = computed(() => {
|
||||||
|
if (currentSort.value && !isHardSort.value) {
|
||||||
|
return [...localResults.value].sort((a: any, b: any) => {
|
||||||
let modifier = 1;
|
let modifier = 1;
|
||||||
let valA = typeof a[this.currentSort] === 'string' ? a[this.currentSort].toLowerCase() : a[this.currentSort];
|
let valA = typeof a[currentSort.value] === 'string' ? a[currentSort.value].toLowerCase() : a[currentSort.value];
|
||||||
if (!isNaN(valA)) valA = Number(valA);
|
if (!isNaN(valA)) valA = Number(valA);
|
||||||
let valB = typeof b[this.currentSort] === 'string' ? b[this.currentSort].toLowerCase() : b[this.currentSort];
|
let valB = typeof b[currentSort.value] === 'string' ? b[currentSort.value].toLowerCase() : b[currentSort.value];
|
||||||
if (!isNaN(valB)) valB = Number(valB);
|
if (!isNaN(valB)) valB = Number(valB);
|
||||||
if (this.currentSortDir === 'desc') modifier = -1;
|
if (currentSortDir.value === 'desc') modifier = -1;
|
||||||
if (valA < valB) return -1 * modifier;
|
if (valA < valB) return -1 * modifier;
|
||||||
if (valA > valB) return 1 * modifier;
|
if (valA > valB) return 1 * modifier;
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return this.localResults;
|
return localResults.value;
|
||||||
},
|
});
|
||||||
resultsWithRows () {
|
|
||||||
return this.results.filter(result => result.rows);
|
const resultsWithRows = computed(() => props.results.filter(result => result.rows));
|
||||||
},
|
const fields = computed(() => resultsWithRows.value.length ? resultsWithRows.value[resultsetIndex.value].fields : []);
|
||||||
fields () {
|
const keyUsage = computed(() => resultsWithRows.value.length ? resultsWithRows.value[resultsetIndex.value].keys : []);
|
||||||
return this.resultsWithRows.length ? this.resultsWithRows[this.resultsetIndex].fields : [];
|
|
||||||
},
|
const fieldsObj = computed(() => {
|
||||||
keyUsage () {
|
if (sortedResults.value.length) {
|
||||||
return this.resultsWithRows.length ? this.resultsWithRows[this.resultsetIndex].keys : [];
|
const fieldsObj: {[key: string]: TableField} = {};
|
||||||
},
|
for (const key in sortedResults.value[0]) {
|
||||||
fieldsObj () {
|
|
||||||
if (this.sortedResults.length) {
|
|
||||||
const fieldsObj = {};
|
|
||||||
for (const key in this.sortedResults[0]) {
|
|
||||||
if (key === '_antares_id') continue;
|
if (key === '_antares_id') continue;
|
||||||
|
|
||||||
const fieldObj = this.fields.find(field => {
|
const fieldObj = fields.value.find(field => {
|
||||||
let fieldNames = [
|
let fieldNames = [
|
||||||
field.name,
|
field.name,
|
||||||
field.alias,
|
field.alias,
|
||||||
@ -245,64 +234,16 @@ export default {
|
|||||||
return fieldsObj;
|
return fieldsObj;
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
results () {
|
|
||||||
this.setLocalResults();
|
|
||||||
this.resultsetIndex = 0;
|
|
||||||
},
|
|
||||||
resultsetIndex () {
|
|
||||||
this.setLocalResults();
|
|
||||||
},
|
|
||||||
isSelected (val) {
|
|
||||||
if (val) this.refreshScroller();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
updated () {
|
|
||||||
if (this.$refs.table)
|
|
||||||
this.refreshScroller();
|
|
||||||
|
|
||||||
if (this.$refs.tableWrapper)
|
|
||||||
this.scrollElement = this.$refs.tableWrapper;
|
|
||||||
|
|
||||||
document.querySelectorAll('.column-resizable').forEach(element => {
|
|
||||||
if (element.clientWidth !== 0)
|
|
||||||
element.style.width = element.clientWidth + 'px';
|
|
||||||
});
|
});
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
window.addEventListener('resize', this.resizeResults);
|
|
||||||
window.addEventListener('keydown', this.onKey);
|
|
||||||
},
|
|
||||||
unmounted () {
|
|
||||||
window.removeEventListener('resize', this.resizeResults);
|
|
||||||
window.removeEventListener('keydown', this.onKey);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
fieldType (cKey) {
|
|
||||||
let type = 'unknown';
|
|
||||||
const field = this.fields.filter(field => field.name === cKey)[0];
|
|
||||||
if (field)
|
|
||||||
type = field.type;
|
|
||||||
|
|
||||||
return type;
|
const fieldLength = (field: TableField) => {
|
||||||
},
|
|
||||||
fieldPrecision (cKey) {
|
|
||||||
let length = 0;
|
|
||||||
const field = this.fields.filter(field => field.name === cKey)[0];
|
|
||||||
if (field)
|
|
||||||
length = field.datePrecision;
|
|
||||||
|
|
||||||
return length;
|
|
||||||
},
|
|
||||||
fieldLength (field) {
|
|
||||||
if ([...BLOB, ...LONG_TEXT].includes(field.type)) return null;
|
if ([...BLOB, ...LONG_TEXT].includes(field.type)) return null;
|
||||||
else if (TEXT.includes(field.type)) return field.charLength;
|
else if (TEXT.includes(field.type)) return field.charLength;
|
||||||
else if (field.numScale) return `${field.numPrecision}, ${field.numScale}`;
|
else if (field.numScale) return `${field.numPrecision}, ${field.numScale}`;
|
||||||
return field.length;
|
return field.length;
|
||||||
},
|
};
|
||||||
keyName (key) {
|
|
||||||
|
const keyName = (key: string) => {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'pri':
|
case 'pri':
|
||||||
return 'PRIMARY';
|
return 'PRIMARY';
|
||||||
@ -313,57 +254,62 @@ export default {
|
|||||||
default:
|
default:
|
||||||
return 'UNKNOWN ' + key;
|
return 'UNKNOWN ' + key;
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
getTable (index) {
|
|
||||||
if (this.resultsWithRows[index] && this.resultsWithRows[index].fields && this.resultsWithRows[index].fields.length)
|
const getTable = (index: number) => {
|
||||||
return this.resultsWithRows[index].fields[0].table;
|
if (resultsWithRows.value[index] && resultsWithRows.value[index].fields && resultsWithRows.value[index].fields.length)
|
||||||
|
return resultsWithRows.value[index].fields[0].table;
|
||||||
return '';
|
return '';
|
||||||
},
|
};
|
||||||
getSchema (index) {
|
|
||||||
if (this.resultsWithRows[index] && this.resultsWithRows[index].fields && this.resultsWithRows[index].fields.length)
|
const getSchema = (index: number) => {
|
||||||
return this.resultsWithRows[index].fields[0].schema;
|
if (resultsWithRows.value[index] && resultsWithRows.value[index].fields && resultsWithRows.value[index].fields.length)
|
||||||
return this.workspaceSchema;
|
return resultsWithRows.value[index].fields[0].schema;
|
||||||
},
|
return workspaceSchema.value;
|
||||||
getPrimaryValue (row) {
|
};
|
||||||
|
|
||||||
|
const getPrimaryValue = (row: any) => {
|
||||||
const primaryFieldName = Object.keys(row).find(prop => [
|
const primaryFieldName = Object.keys(row).find(prop => [
|
||||||
this.primaryField.alias,
|
primaryField.value.alias,
|
||||||
this.primaryField.name,
|
primaryField.value.name,
|
||||||
`${this.primaryField.table}.${this.primaryField.alias}`,
|
`${primaryField.value.table}.${primaryField.value.alias}`,
|
||||||
`${this.primaryField.table}.${this.primaryField.name}`,
|
`${primaryField.value.table}.${primaryField.value.name}`,
|
||||||
`${this.primaryField.tableAlias}.${this.primaryField.alias}`,
|
`${primaryField.value.tableAlias}.${primaryField.value.alias}`,
|
||||||
`${this.primaryField.tableAlias}.${this.primaryField.name}`
|
`${primaryField.value.tableAlias}.${primaryField.value.name}`
|
||||||
].includes(prop));
|
].includes(prop));
|
||||||
return row[primaryFieldName];
|
return row[primaryFieldName];
|
||||||
},
|
};
|
||||||
setLocalResults () {
|
|
||||||
this.localResults = this.resultsWithRows[this.resultsetIndex] && this.resultsWithRows[this.resultsetIndex].rows
|
const setLocalResults = () => {
|
||||||
? this.resultsWithRows[this.resultsetIndex].rows.map(item => {
|
localResults.value = resultsWithRows.value[resultsetIndex.value] && resultsWithRows.value[resultsetIndex.value].rows
|
||||||
|
? resultsWithRows.value[resultsetIndex.value].rows.map(item => {
|
||||||
return { ...item, _antares_id: uidGen() };
|
return { ...item, _antares_id: uidGen() };
|
||||||
})
|
})
|
||||||
: [];
|
: [];
|
||||||
},
|
};
|
||||||
resizeResults () {
|
|
||||||
if (this.$refs.resultTable && this.isSelected) {
|
const resizeResults = () => {
|
||||||
const el = this.$refs.tableWrapper;
|
if (resultTable.value && props.isSelected) {
|
||||||
|
const el = tableWrapper.value;
|
||||||
|
|
||||||
if (el) {
|
if (el) {
|
||||||
const footer = document.getElementById('footer');
|
const footer = document.getElementById('footer');
|
||||||
const size = window.innerHeight - el.getBoundingClientRect().top - footer.offsetHeight;
|
const size = window.innerHeight - el.getBoundingClientRect().top - footer.offsetHeight;
|
||||||
this.resultsSize = size;
|
resultsSize.value = size;
|
||||||
}
|
}
|
||||||
this.$refs.resultTable.updateWindow();
|
resultTable.value.updateWindow();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
refreshScroller () {
|
|
||||||
this.resizeResults();
|
const refreshScroller = () => resizeResults();
|
||||||
},
|
|
||||||
updateField (payload, row) {
|
const updateField = (payload: { field: string; type: string; content: any }, row: {[key: string]: any}) => {
|
||||||
const orgRow = this.localResults.find(lr => lr._antares_id === row._antares_id);
|
const orgRow: any = localResults.value.find((lr: any) => lr._antares_id === row._antares_id);
|
||||||
|
|
||||||
Object.keys(orgRow).forEach(key => { // remap the row
|
Object.keys(orgRow).forEach(key => { // remap the row
|
||||||
if (orgRow[key] instanceof Date && moment(orgRow[key]).isValid()) { // if datetime
|
if (orgRow[key] instanceof Date && moment(orgRow[key]).isValid()) { // if datetime
|
||||||
let datePrecision = '';
|
let datePrecision = '';
|
||||||
const precision = this.fields.find(field => field.name === key).datePrecision;
|
const precision = fields.value.find(field => field.name === key)?.datePrecision;
|
||||||
for (let i = 0; i < precision; i++)
|
for (let i = 0; i < precision; i++)
|
||||||
datePrecision += i === 0 ? '.S' : 'S';
|
datePrecision += i === 0 ? '.S' : 'S';
|
||||||
|
|
||||||
@ -372,80 +318,88 @@ export default {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
primary: this.primaryField.name,
|
primary: primaryField.value.name,
|
||||||
schema: this.getSchema(this.resultsetIndex),
|
schema: getSchema(resultsetIndex.value),
|
||||||
table: this.getTable(this.resultsetIndex),
|
table: getTable(resultsetIndex.value),
|
||||||
id: this.getPrimaryValue(orgRow),
|
id: getPrimaryValue(orgRow),
|
||||||
row,
|
row,
|
||||||
orgRow,
|
orgRow,
|
||||||
...payload
|
...payload
|
||||||
};
|
};
|
||||||
this.$emit('update-field', params);
|
emit('update-field', params);
|
||||||
},
|
};
|
||||||
closeContext () {
|
|
||||||
this.isContext = false;
|
const closeContext = () => {
|
||||||
},
|
isContext.value = false;
|
||||||
showDeleteConfirmModal (e) {
|
};
|
||||||
|
|
||||||
|
const showDeleteConfirmModal = (e: any) => {
|
||||||
if (e && e.path && ['INPUT', 'TEXTAREA', 'SELECT'].includes(e.path[0].tagName))
|
if (e && e.path && ['INPUT', 'TEXTAREA', 'SELECT'].includes(e.path[0].tagName))
|
||||||
return;
|
return;
|
||||||
this.isDeleteConfirmModal = true;
|
isDeleteConfirmModal.value = true;
|
||||||
},
|
};
|
||||||
hideDeleteConfirmModal () {
|
|
||||||
this.isDeleteConfirmModal = false;
|
const hideDeleteConfirmModal = () => {
|
||||||
},
|
isDeleteConfirmModal.value = false;
|
||||||
deleteSelected () {
|
};
|
||||||
this.closeContext();
|
|
||||||
const rows = JSON.parse(JSON.stringify(this.localResults)).filter(row => this.selectedRows.includes(row._antares_id)).map(row => {
|
const deleteSelected = () => {
|
||||||
|
closeContext();
|
||||||
|
const rows = JSON.parse(JSON.stringify(localResults.value)).filter((row: any) => selectedRows.value.includes(row._antares_id)).map((row: any) => {
|
||||||
delete row._antares_id;
|
delete row._antares_id;
|
||||||
return row;
|
return row;
|
||||||
});
|
});
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
primary: this.primaryField.name,
|
primary: primaryField.value.name,
|
||||||
schema: this.getSchema(this.resultsetIndex),
|
schema: getSchema(resultsetIndex.value),
|
||||||
table: this.getTable(this.resultsetIndex),
|
table: getTable(resultsetIndex.value),
|
||||||
rows
|
rows
|
||||||
};
|
};
|
||||||
this.$emit('delete-selected', params);
|
emit('delete-selected', params);
|
||||||
},
|
};
|
||||||
setNull () {
|
|
||||||
const row = this.localResults.find(row => this.selectedRows.includes(row._antares_id));
|
const setNull = () => {
|
||||||
|
const row = localResults.value.find((row: any) => selectedRows.value.includes(row._antares_id));
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
primary: this.primaryField.name,
|
primary: primaryField.value.name,
|
||||||
schema: this.getSchema(this.resultsetIndex),
|
schema: getSchema(resultsetIndex.value),
|
||||||
table: this.getTable(this.resultsetIndex),
|
table: getTable(resultsetIndex.value),
|
||||||
id: this.getPrimaryValue(row),
|
id: getPrimaryValue(row),
|
||||||
row,
|
row,
|
||||||
orgRow: row,
|
orgRow: row,
|
||||||
field: this.selectedCell.field,
|
field: selectedCell.value.field,
|
||||||
content: null
|
content: null as string
|
||||||
};
|
};
|
||||||
this.$emit('update-field', params);
|
emit('update-field', params);
|
||||||
},
|
};
|
||||||
copyCell () {
|
|
||||||
const row = this.localResults.find(row => this.selectedRows.includes(row._antares_id));
|
const copyCell = () => {
|
||||||
|
const row: any = localResults.value.find((row: any) => selectedRows.value.includes(row._antares_id));
|
||||||
const cellName = Object.keys(row).find(prop => [
|
const cellName = Object.keys(row).find(prop => [
|
||||||
this.selectedCell.field,
|
selectedCell.value.field,
|
||||||
this.selectedCell.orgField,
|
selectedCell.value.orgField,
|
||||||
`${this.fields[0].table}.${this.selectedCell.field}`,
|
`${fields.value[0].table}.${selectedCell.value.field}`,
|
||||||
`${this.fields[0].tableAlias}.${this.selectedCell.field}`
|
`${fields.value[0].tableAlias}.${selectedCell.value.field}`
|
||||||
].includes(prop));
|
].includes(prop));
|
||||||
let valueToCopy = row[cellName];
|
let valueToCopy = row[cellName];
|
||||||
if (typeof valueToCopy === 'object')
|
if (typeof valueToCopy === 'object')
|
||||||
valueToCopy = JSON.stringify(valueToCopy);
|
valueToCopy = JSON.stringify(valueToCopy);
|
||||||
navigator.clipboard.writeText(valueToCopy);
|
navigator.clipboard.writeText(valueToCopy);
|
||||||
},
|
};
|
||||||
copyRow () {
|
|
||||||
const row = this.localResults.find(row => this.selectedRows.includes(row._antares_id));
|
const copyRow = () => {
|
||||||
|
const row = localResults.value.find((row: any) => selectedRows.value.includes(row._antares_id));
|
||||||
const rowToCopy = JSON.parse(JSON.stringify(row));
|
const rowToCopy = JSON.parse(JSON.stringify(row));
|
||||||
delete rowToCopy._antares_id;
|
delete rowToCopy._antares_id;
|
||||||
navigator.clipboard.writeText(JSON.stringify(rowToCopy));
|
navigator.clipboard.writeText(JSON.stringify(rowToCopy));
|
||||||
},
|
};
|
||||||
applyUpdate (params) {
|
|
||||||
|
const applyUpdate = (params: TableUpdateParams) => {
|
||||||
const { primary, id, field, table, content } = params;
|
const { primary, id, field, table, content } = params;
|
||||||
|
|
||||||
this.localResults = this.localResults.map(row => {
|
localResults.value = localResults.value.map((row: any) => {
|
||||||
if (row[primary] === id)// only fieldName
|
if (row[primary] === id)// only fieldName
|
||||||
row[field] = content;
|
row[field] = content;
|
||||||
else if (row[`${table}.${primary}`] === id)// table.fieldName
|
else if (row[`${table}.${primary}`] === id)// table.fieldName
|
||||||
@ -453,92 +407,100 @@ export default {
|
|||||||
|
|
||||||
return row;
|
return row;
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
selectRow (event, row, field) {
|
|
||||||
this.selectedField = field;
|
const selectRow = (event: KeyboardEvent, row: any, field: string) => {
|
||||||
|
selectedField.value = field;
|
||||||
const selectedRowId = row._antares_id;
|
const selectedRowId = row._antares_id;
|
||||||
|
|
||||||
if (event.ctrlKey || event.metaKey) {
|
if (event.ctrlKey || event.metaKey) {
|
||||||
if (this.selectedRows.includes(selectedRowId))
|
if (selectedRows.value.includes(selectedRowId))
|
||||||
this.selectedRows = this.selectedRows.filter(el => el !== selectedRowId);
|
selectedRows.value = selectedRows.value.filter(el => el !== selectedRowId);
|
||||||
else
|
else
|
||||||
this.selectedRows.push(selectedRowId);
|
selectedRows.value.push(selectedRowId);
|
||||||
}
|
}
|
||||||
else if (event.shiftKey) {
|
else if (event.shiftKey) {
|
||||||
if (!this.selectedRows.length)
|
if (!selectedRows.value.length)
|
||||||
this.selectedRows.push(selectedRowId);
|
selectedRows.value.push(selectedRowId);
|
||||||
else {
|
else {
|
||||||
const lastID = this.selectedRows.slice(-1)[0];
|
const lastID = selectedRows.value.slice(-1)[0];
|
||||||
const lastIndex = this.sortedResults.findIndex(el => el._antares_id === lastID);
|
const lastIndex = sortedResults.value.findIndex((el: any) => el._antares_id === lastID);
|
||||||
const clickedIndex = this.sortedResults.findIndex(el => el._antares_id === selectedRowId);
|
const clickedIndex = sortedResults.value.findIndex((el: any) => el._antares_id === selectedRowId);
|
||||||
if (lastIndex > clickedIndex) {
|
if (lastIndex > clickedIndex) {
|
||||||
for (let i = clickedIndex; i < lastIndex; i++)
|
for (let i = clickedIndex; i < lastIndex; i++)
|
||||||
this.selectedRows.push(this.sortedResults[i]._antares_id);
|
selectedRows.value.push((sortedResults.value[i] as any)._antares_id);
|
||||||
}
|
}
|
||||||
else if (lastIndex < clickedIndex) {
|
else if (lastIndex < clickedIndex) {
|
||||||
for (let i = clickedIndex; i > lastIndex; i--)
|
for (let i = clickedIndex; i > lastIndex; i--)
|
||||||
this.selectedRows.push(this.sortedResults[i]._antares_id);
|
selectedRows.value.push((sortedResults.value[i] as any)._antares_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.selectedRows = [selectedRowId];
|
selectedRows.value = [selectedRowId];
|
||||||
},
|
};
|
||||||
selectAllRows (e) {
|
|
||||||
if (e.target.classList.contains('editable-field')) return;
|
|
||||||
|
|
||||||
this.selectedField = 0;
|
const selectAllRows = (e: KeyboardEvent) => {
|
||||||
this.selectedRows = this.localResults.reduce((acc, curr) => {
|
if ((e.target as HTMLElement).classList.contains('editable-field')) return;
|
||||||
|
|
||||||
|
selectedField.value = 0;
|
||||||
|
selectedRows.value = localResults.value.reduce((acc, curr: any) => {
|
||||||
acc.push(curr._antares_id);
|
acc.push(curr._antares_id);
|
||||||
return acc;
|
return acc;
|
||||||
}, []);
|
}, []);
|
||||||
},
|
};
|
||||||
deselectRows () {
|
|
||||||
if (!this.isEditingRow)
|
|
||||||
this.selectedRows = [];
|
|
||||||
},
|
|
||||||
contextMenu (event, cell) {
|
|
||||||
if (event.target.localName === 'input') return;
|
|
||||||
|
|
||||||
this.selectedCell = cell;
|
const deselectRows = () => {
|
||||||
if (!this.selectedRows.includes(cell.id))
|
if (!isEditingRow.value)
|
||||||
this.selectedRows = [cell.id];
|
selectedRows.value = [];
|
||||||
this.contextEvent = event;
|
};
|
||||||
this.isContext = true;
|
|
||||||
},
|
|
||||||
sort (field) {
|
|
||||||
if (!this.isSortable) return;
|
|
||||||
|
|
||||||
this.selectedRows = [];
|
const contextMenu = (event: MouseEvent, cell: any) => {
|
||||||
|
if ((event.target as HTMLElement).localName === 'input') return;
|
||||||
|
|
||||||
if (this.mode === 'query')
|
selectedCell.value = cell;
|
||||||
field = `${this.getTable(this.resultsetIndex)}.${field}`;
|
if (!selectedRows.value.includes(cell.id))
|
||||||
|
selectedRows.value = [cell.id];
|
||||||
|
contextEvent.value = event;
|
||||||
|
isContext.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
if (field === this.currentSort) {
|
const sort = (field: string) => {
|
||||||
if (this.currentSortDir === 'asc')
|
if (!isSortable.value) return;
|
||||||
this.currentSortDir = 'desc';
|
|
||||||
|
selectedRows.value = [];
|
||||||
|
|
||||||
|
if (props.mode === 'query')
|
||||||
|
field = `${getTable(resultsetIndex.value)}.${field}`;
|
||||||
|
|
||||||
|
if (field === currentSort.value) {
|
||||||
|
if (currentSortDir.value === 'asc')
|
||||||
|
currentSortDir.value = 'desc';
|
||||||
else
|
else
|
||||||
this.resetSort();
|
resetSort();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.currentSortDir = 'asc';
|
currentSortDir.value = 'asc';
|
||||||
this.currentSort = field;
|
currentSort.value = field;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isHardSort)
|
if (isHardSort.value)
|
||||||
this.$emit('hard-sort', { field: this.currentSort, dir: this.currentSortDir });
|
emit('hard-sort', { field: currentSort.value, dir: currentSortDir.value });
|
||||||
},
|
};
|
||||||
resetSort () {
|
|
||||||
this.currentSort = '';
|
|
||||||
this.currentSortDir = 'asc';
|
|
||||||
},
|
|
||||||
selectResultset (index) {
|
|
||||||
this.resultsetIndex = index;
|
|
||||||
},
|
|
||||||
downloadTable (format, filename) {
|
|
||||||
if (!this.sortedResults) return;
|
|
||||||
|
|
||||||
const rows = JSON.parse(JSON.stringify(this.sortedResults)).map(row => {
|
const resetSort = () => {
|
||||||
|
currentSort.value = '';
|
||||||
|
currentSortDir.value = 'asc';
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectResultset = (index: number) => {
|
||||||
|
resultsetIndex.value = index;
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadTable = (format: 'csv' | 'json', filename: string) => {
|
||||||
|
if (!sortedResults.value) return;
|
||||||
|
|
||||||
|
const rows = JSON.parse(JSON.stringify(sortedResults.value)).map((row: any) => {
|
||||||
delete row._antares_id;
|
delete row._antares_id;
|
||||||
return row;
|
return row;
|
||||||
});
|
});
|
||||||
@ -548,29 +510,30 @@ export default {
|
|||||||
content: rows,
|
content: rows,
|
||||||
filename
|
filename
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
onKey (e) {
|
|
||||||
if (!this.isSelected)
|
const onKey = async (e: KeyboardEvent) => {
|
||||||
|
if (!props.isSelected)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (this.isEditingRow)
|
if (isEditingRow.value)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ((e.ctrlKey || e.metaKey) && e.code === 'KeyA' && !e.altKey)
|
if ((e.ctrlKey || e.metaKey) && e.code === 'KeyA' && !e.altKey)
|
||||||
this.selectAllRows(e);
|
selectAllRows(e);
|
||||||
|
|
||||||
// row naviation stuff
|
// row navigation stuff
|
||||||
if ((e.code.includes('Arrow') || e.code === 'Tab') && this.sortedResults.length > 0 && !e.altKey) {
|
if ((e.code.includes('Arrow') || e.code === 'Tab') && sortedResults.value.length > 0 && !e.altKey) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const aviableFields= Object.keys(this.sortedResults[0]).slice(0, -1); // removes _antares_id
|
const aviableFields= Object.keys(sortedResults.value[0]).slice(0, -1); // removes _antares_id
|
||||||
|
|
||||||
if (!this.selectedField)
|
if (!selectedField.value)
|
||||||
this.selectedField = aviableFields[0];
|
selectedField.value = aviableFields[0];
|
||||||
|
|
||||||
const selectedId = this.selectedRows[0];
|
const selectedId = selectedRows.value[0];
|
||||||
const selectedIndex = this.sortedResults.findIndex(row => row._antares_id === selectedId);
|
const selectedIndex = sortedResults.value.findIndex((row: any) => row._antares_id === selectedId);
|
||||||
const selectedFieldIndex = aviableFields.findIndex(field => field === this.selectedField);
|
const selectedFieldIndex = aviableFields.findIndex(field => field === selectedField.value);
|
||||||
let nextIndex = 0;
|
let nextIndex = 0;
|
||||||
let nextFieldIndex = 0;
|
let nextFieldIndex = 0;
|
||||||
|
|
||||||
@ -580,8 +543,8 @@ export default {
|
|||||||
nextIndex = selectedIndex + 1;
|
nextIndex = selectedIndex + 1;
|
||||||
nextFieldIndex = selectedFieldIndex;
|
nextFieldIndex = selectedFieldIndex;
|
||||||
|
|
||||||
if (nextIndex > this.sortedResults.length -1)
|
if (nextIndex > sortedResults.value.length -1)
|
||||||
nextIndex = this.sortedResults.length -1;
|
nextIndex = sortedResults.value.length -1;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 'ArrowUp':
|
case 'ArrowUp':
|
||||||
@ -626,38 +589,78 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.sortedResults[nextIndex] && nextIndex !== selectedIndex) {
|
if (sortedResults.value[nextIndex] && nextIndex !== selectedIndex) {
|
||||||
this.selectedRows = [this.sortedResults[nextIndex]._antares_id];
|
selectedRows.value = [(sortedResults.value[nextIndex] as any)._antares_id];
|
||||||
this.$nextTick(() => this.scrollToCell(this.scrollElement.querySelector('.td.selected')));
|
await nextTick();
|
||||||
|
scrollToCell(scrollElement.value.querySelector('.td.selected'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aviableFields[nextFieldIndex] && nextFieldIndex !== selectedFieldIndex) {
|
if (aviableFields[nextFieldIndex] && nextFieldIndex !== selectedFieldIndex) {
|
||||||
this.selectedField = aviableFields[nextFieldIndex];
|
selectedField.value = aviableFields[nextFieldIndex];
|
||||||
this.$nextTick(() => this.scrollToCell(this.scrollElement.querySelector('.td.selected')));
|
await nextTick();
|
||||||
}
|
scrollToCell(scrollElement.value.querySelector('.td.selected'));
|
||||||
}
|
|
||||||
},
|
|
||||||
scrollToCell (el) {
|
|
||||||
if (!el) return;
|
|
||||||
const visYMin = this.scrollElement.scrollTop;
|
|
||||||
const visYMax = this.scrollElement.scrollTop + this.scrollElement.clientHeight - el.clientHeight;
|
|
||||||
const visXMin = this.scrollElement.scrollLeft;
|
|
||||||
const visXMax = this.scrollElement.scrollLeft + this.scrollElement.clientWidth - el.clientWidth;
|
|
||||||
|
|
||||||
if (el.offsetTop < visYMin)
|
|
||||||
this.scrollElement.scrollTop = el.offsetTop;
|
|
||||||
|
|
||||||
else if (el.offsetTop >= visYMax)
|
|
||||||
this.scrollElement.scrollTop = el.offsetTop - this.scrollElement.clientHeight + el.clientHeight;
|
|
||||||
|
|
||||||
if (el.offsetLeft < visXMin)
|
|
||||||
this.scrollElement.scrollLeft = el.offsetLeft;
|
|
||||||
|
|
||||||
else if (el.offsetLeft >= visXMax)
|
|
||||||
this.scrollElement.scrollLeft = el.offsetLeft - this.scrollElement.clientWidth + el.clientWidth;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const scrollToCell = (el: HTMLElement) => {
|
||||||
|
if (!el) return;
|
||||||
|
const visYMin = scrollElement.value.scrollTop;
|
||||||
|
const visYMax = scrollElement.value.scrollTop + scrollElement.value.clientHeight - el.clientHeight;
|
||||||
|
const visXMin = scrollElement.value.scrollLeft;
|
||||||
|
const visXMax = scrollElement.value.scrollLeft + scrollElement.value.clientWidth - el.clientWidth;
|
||||||
|
|
||||||
|
if (el.offsetTop < visYMin)
|
||||||
|
scrollElement.value.scrollTop = el.offsetTop;
|
||||||
|
|
||||||
|
else if (el.offsetTop >= visYMax)
|
||||||
|
scrollElement.value.scrollTop = el.offsetTop - scrollElement.value.clientHeight + el.clientHeight;
|
||||||
|
|
||||||
|
if (el.offsetLeft < visXMin)
|
||||||
|
scrollElement.value.scrollLeft = el.offsetLeft;
|
||||||
|
|
||||||
|
else if (el.offsetLeft >= visXMax)
|
||||||
|
scrollElement.value.scrollLeft = el.offsetLeft - scrollElement.value.clientWidth + el.clientWidth;
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({ applyUpdate, refreshScroller, resetSort, resizeResults, downloadTable });
|
||||||
|
|
||||||
|
watch(() => props.results, () => {
|
||||||
|
setLocalResults();
|
||||||
|
resultsetIndex.value = 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(resultsetIndex, () => {
|
||||||
|
setLocalResults();
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => props.isSelected, (val) => {
|
||||||
|
if (val) refreshScroller();
|
||||||
|
});
|
||||||
|
|
||||||
|
onUpdated(() => {
|
||||||
|
if (table.value)
|
||||||
|
refreshScroller();
|
||||||
|
|
||||||
|
if (tableWrapper.value)
|
||||||
|
scrollElement.value = tableWrapper.value;
|
||||||
|
|
||||||
|
document.querySelectorAll<HTMLElement>('.column-resizable').forEach(element => {
|
||||||
|
if (element.clientWidth !== 0)
|
||||||
|
element.style.width = element.clientWidth + 'px';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('resize', resizeResults);
|
||||||
|
window.addEventListener('keydown', onKey);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('resize', resizeResults);
|
||||||
|
window.removeEventListener('keydown', onKey);
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
@close-context="closeContext"
|
@close-context="closeContext"
|
||||||
>
|
>
|
||||||
<div v-if="selectedRows.length === 1" class="context-element">
|
<div v-if="selectedRows.length === 1" class="context-element">
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-content-copy text-light pr-1" /> {{ $t('word.copy') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-content-copy text-light pr-1" /> {{ t('word.copy') }}</span>
|
||||||
<i class="mdi mdi-18px mdi-chevron-right text-light pl-1" />
|
<i class="mdi mdi-18px mdi-chevron-right text-light pl-1" />
|
||||||
<div class="context-submenu">
|
<div class="context-submenu">
|
||||||
<div
|
<div
|
||||||
@ -13,7 +13,7 @@
|
|||||||
@click="copyCell"
|
@click="copyCell"
|
||||||
>
|
>
|
||||||
<span class="d-flex">
|
<span class="d-flex">
|
||||||
<i class="mdi mdi-18px mdi-numeric-0 mdi-rotate-90 text-light pr-1" /> {{ $tc('word.cell', 1) }}
|
<i class="mdi mdi-18px mdi-numeric-0 mdi-rotate-90 text-light pr-1" /> {{ t('word.cell', 1) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@ -22,7 +22,7 @@
|
|||||||
@click="copyRow"
|
@click="copyRow"
|
||||||
>
|
>
|
||||||
<span class="d-flex">
|
<span class="d-flex">
|
||||||
<i class="mdi mdi-18px mdi-table-row text-light pr-1" /> {{ $tc('word.row', 1) }}
|
<i class="mdi mdi-18px mdi-table-row text-light pr-1" /> {{ t('word.row', 1) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -33,7 +33,7 @@
|
|||||||
@click="setNull"
|
@click="setNull"
|
||||||
>
|
>
|
||||||
<span class="d-flex">
|
<span class="d-flex">
|
||||||
<i class="mdi mdi-18px mdi-null text-light pr-1" /> {{ $t('message.setNull') }}
|
<i class="mdi mdi-18px mdi-null text-light pr-1" /> {{ t('message.setNull') }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@ -42,45 +42,46 @@
|
|||||||
@click="showConfirmModal"
|
@click="showConfirmModal"
|
||||||
>
|
>
|
||||||
<span class="d-flex">
|
<span class="d-flex">
|
||||||
<i class="mdi mdi-18px mdi-delete text-light pr-1" /> {{ $tc('message.deleteRows', selectedRows.length) }}
|
<i class="mdi mdi-18px mdi-delete text-light pr-1" /> {{ t('message.deleteRows', selectedRows.length) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</BaseContextMenu>
|
</BaseContextMenu>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import BaseContextMenu from '@/components/BaseContextMenu';
|
import BaseContextMenu from '@/components/BaseContextMenu.vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
export default {
|
const { t } = useI18n();
|
||||||
name: 'WorkspaceTabQueryTableContext',
|
|
||||||
components: {
|
defineProps({
|
||||||
BaseContextMenu
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
contextEvent: MouseEvent,
|
contextEvent: MouseEvent,
|
||||||
selectedRows: Array,
|
selectedRows: Array,
|
||||||
selectedCell: Object
|
selectedCell: Object
|
||||||
},
|
});
|
||||||
emits: ['show-delete-modal', 'close-context', 'set-null', 'copy-cell', 'copy-row'],
|
|
||||||
methods: {
|
const emit = defineEmits(['show-delete-modal', 'close-context', 'set-null', 'copy-cell', 'copy-row']);
|
||||||
showConfirmModal () {
|
|
||||||
this.$emit('show-delete-modal');
|
const showConfirmModal = () => {
|
||||||
},
|
emit('show-delete-modal');
|
||||||
closeContext () {
|
};
|
||||||
this.$emit('close-context');
|
|
||||||
},
|
const closeContext = () => {
|
||||||
setNull () {
|
emit('close-context');
|
||||||
this.$emit('set-null');
|
};
|
||||||
this.closeContext();
|
|
||||||
},
|
const setNull = () => {
|
||||||
copyCell () {
|
emit('set-null');
|
||||||
this.$emit('copy-cell');
|
closeContext();
|
||||||
this.closeContext();
|
};
|
||||||
},
|
|
||||||
copyRow () {
|
const copyCell = () => {
|
||||||
this.$emit('copy-row');
|
emit('copy-cell');
|
||||||
this.closeContext();
|
closeContext();
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
const copyRow = () => {
|
||||||
|
emit('copy-row');
|
||||||
|
closeContext();
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
class="cell-content"
|
class="cell-content"
|
||||||
:class="`${isNull(col)} ${typeClass(fields[cKey].type)}`"
|
:class="`${isNull(col)} ${typeClass(fields[cKey].type)}`"
|
||||||
@dblclick="editON(cKey)"
|
@dblclick="editON(cKey)"
|
||||||
>{{ cutText(typeFormat(col, fields[cKey].type.toLowerCase(), fields[cKey].length)) }}</span>
|
>{{ cutText(typeFormat(col, fields[cKey].type.toLowerCase(), fields[cKey].length) as string) }}</span>
|
||||||
<ForeignKeySelect
|
<ForeignKeySelect
|
||||||
v-else-if="isForeignKey(cKey)"
|
v-else-if="isForeignKey(cKey)"
|
||||||
v-model="editingContent"
|
v-model="editingContent"
|
||||||
@ -68,14 +68,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
v-if="isTextareaEditor"
|
v-if="isTextareaEditor"
|
||||||
:confirm-text="$t('word.update')"
|
:confirm-text="t('word.update')"
|
||||||
size="medium"
|
size="medium"
|
||||||
@confirm="editOFF"
|
@confirm="editOFF"
|
||||||
@hide="hideEditorModal"
|
@hide="hideEditorModal"
|
||||||
>
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-playlist-edit mr-1" /> <span class="cut-text">{{ $t('word.edit') }} "{{ editingField }}"</span>
|
<i class="mdi mdi-24px mdi-playlist-edit mr-1" /> <span class="cut-text">{{ t('word.edit') }} "{{ editingField }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #body>
|
<template #body>
|
||||||
@ -90,7 +90,7 @@
|
|||||||
<div class="editor-field-info p-vcentered">
|
<div class="editor-field-info p-vcentered">
|
||||||
<div class="d-flex p-vcentered">
|
<div class="d-flex p-vcentered">
|
||||||
<label for="editorMode" class="form-label mr-2">
|
<label for="editorMode" class="form-label mr-2">
|
||||||
<b>{{ $t('word.content') }}</b>:
|
<b>{{ t('word.content') }}</b>:
|
||||||
</label>
|
</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
id="editorMode"
|
id="editorMode"
|
||||||
@ -104,10 +104,10 @@
|
|||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<div class="p-vcentered">
|
<div class="p-vcentered">
|
||||||
<div class="mr-4">
|
<div class="mr-4">
|
||||||
<b>{{ $t('word.size') }}</b>: {{ editingContent ? editingContent.length : 0 }}
|
<b>{{ t('word.size') }}</b>: {{ editingContent ? editingContent.length : 0 }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="editingType">
|
<div v-if="editingType">
|
||||||
<b>{{ $t('word.type') }}</b>: {{ editingType.toUpperCase() }}
|
<b>{{ t('word.type') }}</b>: {{ editingType.toUpperCase() }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -132,14 +132,14 @@
|
|||||||
</ConfirmModal>
|
</ConfirmModal>
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
v-if="isBlobEditor"
|
v-if="isBlobEditor"
|
||||||
:confirm-text="$t('word.update')"
|
:confirm-text="t('word.update')"
|
||||||
@confirm="editOFF"
|
@confirm="editOFF"
|
||||||
@hide="hideEditorModal"
|
@hide="hideEditorModal"
|
||||||
>
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-playlist-edit mr-1" />
|
<i class="mdi mdi-24px mdi-playlist-edit mr-1" />
|
||||||
<span class="cut-text">{{ $t('word.edit') }} "{{ editingField }}"</span>
|
<span class="cut-text">{{ t('word.edit') }} "{{ editingField }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #body>
|
<template #body>
|
||||||
@ -156,11 +156,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="editor-buttons mt-2">
|
<div class="editor-buttons mt-2">
|
||||||
<button class="btn btn-link btn-sm" @click="downloadFile">
|
<button class="btn btn-link btn-sm" @click="downloadFile">
|
||||||
<span>{{ $t('word.download') }}</span>
|
<span>{{ t('word.download') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-download ml-1" />
|
<i class="mdi mdi-24px mdi-download ml-1" />
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-link btn-sm" @click="prepareToDelete">
|
<button class="btn btn-link btn-sm" @click="prepareToDelete">
|
||||||
<span>{{ $t('word.delete') }}</span>
|
<span>{{ t('word.delete') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-delete-forever ml-1" />
|
<i class="mdi mdi-24px mdi-delete-forever ml-1" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -168,19 +168,19 @@
|
|||||||
</Transition>
|
</Transition>
|
||||||
<div class="editor-field-info">
|
<div class="editor-field-info">
|
||||||
<div>
|
<div>
|
||||||
<b>{{ $t('word.size') }}</b>: {{ formatBytes(editingContent.length) }}<br>
|
<b>{{ t('word.size') }}</b>: {{ formatBytes(editingContent.length) }}<br>
|
||||||
<b>{{ $t('word.mimeType') }}</b>: {{ contentInfo.mime }}
|
<b>{{ t('word.mimeType') }}</b>: {{ contentInfo.mime }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="editingType">
|
<div v-if="editingType">
|
||||||
<b>{{ $t('word.type') }}</b>: {{ editingType.toUpperCase() }}
|
<b>{{ t('word.type') }}</b>: {{ editingType.toUpperCase() }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
<label>{{ $t('message.uploadFile') }}</label>
|
<label>{{ t('message.uploadFile') }}</label>
|
||||||
<input
|
<input
|
||||||
class="form-input"
|
class="form-input"
|
||||||
type="file"
|
type="file"
|
||||||
@change="filesChange($event)"
|
@change="filesChange($event as any)"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -189,13 +189,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import moment from 'moment';
|
import { computed, onBeforeUnmount, Prop, ref, Ref, watch, nextTick } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import * as moment from 'moment';
|
||||||
import { ModelOperations } from '@vscode/vscode-languagedetection';
|
import { ModelOperations } from '@vscode/vscode-languagedetection';
|
||||||
import { mimeFromHex } from 'common/libs/mimeFromHex';
|
import { mimeFromHex } from 'common/libs/mimeFromHex';
|
||||||
import { formatBytes } from 'common/libs/formatBytes';
|
import { formatBytes } from 'common/libs/formatBytes';
|
||||||
import { bufferToBase64 } from 'common/libs/bufferToBase64';
|
import { bufferToBase64 } from 'common/libs/bufferToBase64';
|
||||||
import hexToBinary from 'common/libs/hexToBinary';
|
import hexToBinary, { HexChar } from 'common/libs/hexToBinary';
|
||||||
import {
|
import {
|
||||||
TEXT,
|
TEXT,
|
||||||
LONG_TEXT,
|
LONG_TEXT,
|
||||||
@ -213,52 +215,49 @@ import {
|
|||||||
SPATIAL,
|
SPATIAL,
|
||||||
IS_MULTI_SPATIAL
|
IS_MULTI_SPATIAL
|
||||||
} from 'common/fieldTypes';
|
} from 'common/fieldTypes';
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
import ConfirmModal from '@/components/BaseConfirmModal.vue';
|
||||||
import TextEditor from '@/components/BaseTextEditor';
|
import TextEditor from '@/components/BaseTextEditor.vue';
|
||||||
import BaseMap from '@/components/BaseMap';
|
import BaseMap from '@/components/BaseMap.vue';
|
||||||
import ForeignKeySelect from '@/components/ForeignKeySelect';
|
import ForeignKeySelect from '@/components/ForeignKeySelect.vue';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
import { QueryForeign, TableField } from 'common/interfaces/antares';
|
||||||
|
|
||||||
export default {
|
const { t } = useI18n();
|
||||||
name: 'WorkspaceTabQueryTableRow',
|
|
||||||
components: {
|
const props = defineProps({
|
||||||
ConfirmModal,
|
|
||||||
TextEditor,
|
|
||||||
ForeignKeySelect,
|
|
||||||
BaseMap,
|
|
||||||
BaseSelect
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
row: Object,
|
row: Object,
|
||||||
fields: Object,
|
fields: Object as Prop<{
|
||||||
keyUsage: Array,
|
[key: string]: TableField;
|
||||||
|
}>,
|
||||||
|
keyUsage: Array as Prop<QueryForeign[]>,
|
||||||
itemHeight: Number,
|
itemHeight: Number,
|
||||||
elementType: { type: String, default: 'table' },
|
elementType: { type: String, default: 'table' },
|
||||||
selected: { type: Boolean, default: false },
|
selected: { type: Boolean, default: false },
|
||||||
selectedCell: { type: String, default: null }
|
selectedCell: { type: String, default: null }
|
||||||
},
|
});
|
||||||
emits: ['update-field', 'select-row', 'contextmenu', 'start-editing', 'stop-editing'],
|
|
||||||
data () {
|
const emit = defineEmits(['update-field', 'select-row', 'contextmenu', 'start-editing', 'stop-editing']);
|
||||||
return {
|
|
||||||
isInlineEditor: {},
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
isTextareaEditor: false,
|
const isInlineEditor: Ref<any> = ref({});
|
||||||
isBlobEditor: false,
|
const isTextareaEditor = ref(false);
|
||||||
isMapModal: false,
|
const isBlobEditor = ref(false);
|
||||||
isMultiSpatial: false,
|
const isMapModal = ref(false);
|
||||||
willBeDeleted: false,
|
const isMultiSpatial = ref(false);
|
||||||
originalContent: null,
|
const willBeDeleted = ref(false);
|
||||||
editingContent: null,
|
const originalContent = ref(null);
|
||||||
editingType: null,
|
const editingContent = ref(null);
|
||||||
editingField: null,
|
const editingType = ref(null);
|
||||||
editingLength: null,
|
const editingField = ref(null);
|
||||||
editorMode: 'text',
|
const editingLength = ref(null);
|
||||||
contentInfo: {
|
const editorMode = ref('text');
|
||||||
|
const contentInfo = ref({
|
||||||
ext: '',
|
ext: '',
|
||||||
mime: '',
|
mime: '',
|
||||||
size: null
|
size: null
|
||||||
},
|
});
|
||||||
fileToUpload: null,
|
const fileToUpload = ref(null);
|
||||||
availableLanguages: [
|
const availableLanguages = ref([
|
||||||
{ name: 'TEXT', slug: 'text', id: 'text' },
|
{ name: 'TEXT', slug: 'text', id: 'text' },
|
||||||
{ name: 'HTML', slug: 'html', id: 'html' },
|
{ name: 'HTML', slug: 'html', id: 'html' },
|
||||||
{ name: 'XML', slug: 'xml', id: 'xml' },
|
{ name: 'XML', slug: 'xml', id: 'xml' },
|
||||||
@ -267,313 +266,291 @@ export default {
|
|||||||
{ name: 'INI', slug: 'ini', id: 'ini' },
|
{ name: 'INI', slug: 'ini', id: 'ini' },
|
||||||
{ name: 'MARKDOWN', slug: 'markdown', id: 'md' },
|
{ name: 'MARKDOWN', slug: 'markdown', id: 'md' },
|
||||||
{ name: 'YAML', slug: 'yaml', id: 'yaml' }
|
{ name: 'YAML', slug: 'yaml', id: 'yaml' }
|
||||||
]
|
]);
|
||||||
};
|
|
||||||
},
|
const inputProps = computed(() => {
|
||||||
computed: {
|
if ([...TEXT, ...LONG_TEXT].includes(editingType.value))
|
||||||
inputProps () {
|
|
||||||
if ([...TEXT, ...LONG_TEXT].includes(this.editingType))
|
|
||||||
return { type: 'text', mask: false };
|
return { type: 'text', mask: false };
|
||||||
|
|
||||||
if ([...NUMBER, ...FLOAT].includes(this.editingType))
|
if ([...NUMBER, ...FLOAT].includes(editingType.value))
|
||||||
return { type: 'number', mask: false };
|
return { type: 'number', mask: false };
|
||||||
|
|
||||||
if (TIME.includes(this.editingType)) {
|
if (TIME.includes(editingType.value)) {
|
||||||
let timeMask = '##:##:##';
|
let timeMask = '##:##:##';
|
||||||
const precision = this.fields[this.editingField].length;
|
const precision = props.fields[editingField.value].length;
|
||||||
|
|
||||||
for (let i = 0; i < precision; i++)
|
for (let i = 0; i < precision; i++)
|
||||||
timeMask += i === 0 ? '.#' : '#';
|
timeMask += i === 0 ? '.#' : '#';
|
||||||
|
|
||||||
if (HAS_TIMEZONE.includes(this.editingType))
|
if (HAS_TIMEZONE.includes(editingType.value))
|
||||||
timeMask += 'X##';
|
timeMask += 'X##';
|
||||||
|
|
||||||
return { type: 'text', mask: timeMask };
|
return { type: 'text', mask: timeMask };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DATE.includes(this.editingType))
|
if (DATE.includes(editingType.value))
|
||||||
return { type: 'text', mask: '####-##-##' };
|
return { type: 'text', mask: '####-##-##' };
|
||||||
|
|
||||||
if (DATETIME.includes(this.editingType)) {
|
if (DATETIME.includes(editingType.value)) {
|
||||||
let datetimeMask = '####-##-## ##:##:##';
|
let datetimeMask = '####-##-## ##:##:##';
|
||||||
const precision = this.fields[this.editingField].length;
|
const precision = props.fields[editingField.value].length;
|
||||||
|
|
||||||
for (let i = 0; i < precision; i++)
|
for (let i = 0; i < precision; i++)
|
||||||
datetimeMask += i === 0 ? '.#' : '#';
|
datetimeMask += i === 0 ? '.#' : '#';
|
||||||
|
|
||||||
if (HAS_TIMEZONE.includes(this.editingType))
|
if (HAS_TIMEZONE.includes(editingType.value))
|
||||||
datetimeMask += 'X##';
|
datetimeMask += 'X##';
|
||||||
|
|
||||||
return { type: 'text', mask: datetimeMask };
|
return { type: 'text', mask: datetimeMask };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BLOB.includes(this.editingType))
|
if (BLOB.includes(editingType.value))
|
||||||
return { type: 'file', mask: false };
|
return { type: 'file', mask: false };
|
||||||
|
|
||||||
if (BOOLEAN.includes(this.editingType))
|
if (BOOLEAN.includes(editingType.value))
|
||||||
return { type: 'boolean', mask: false };
|
return { type: 'boolean', mask: false };
|
||||||
|
|
||||||
if (SPATIAL.includes(this.editingType))
|
if (SPATIAL.includes(editingType.value))
|
||||||
return { type: 'map', mask: false };
|
return { type: 'map', mask: false };
|
||||||
|
|
||||||
return { type: 'text', mask: false };
|
return { type: 'text', mask: false };
|
||||||
},
|
});
|
||||||
isImage () {
|
|
||||||
return ['gif', 'jpg', 'png', 'bmp', 'ico', 'tif'].includes(this.contentInfo.ext);
|
|
||||||
},
|
|
||||||
foreignKeys () {
|
|
||||||
return this.keyUsage.map(key => key.field);
|
|
||||||
},
|
|
||||||
isEditable () {
|
|
||||||
if (this.elementType === 'view') return false;
|
|
||||||
|
|
||||||
if (this.fields) {
|
const isImage = computed(() => {
|
||||||
const nElements = Object.keys(this.fields).reduce((acc, curr) => {
|
return ['gif', 'jpg', 'png', 'bmp', 'ico', 'tif'].includes(contentInfo.value.ext);
|
||||||
acc.add(this.fields[curr].table);
|
});
|
||||||
acc.add(this.fields[curr].schema);
|
|
||||||
|
const foreignKeys = computed(() => {
|
||||||
|
return props.keyUsage.map(key => key.field);
|
||||||
|
});
|
||||||
|
|
||||||
|
const isEditable = computed(() => {
|
||||||
|
if (props.elementType === 'view') return false;
|
||||||
|
|
||||||
|
if (props.fields) {
|
||||||
|
const nElements = Object.keys(props.fields).reduce((acc, curr) => {
|
||||||
|
acc.add(props.fields[curr].table);
|
||||||
|
acc.add(props.fields[curr].schema);
|
||||||
return acc;
|
return acc;
|
||||||
}, new Set([]));
|
}, new Set([]));
|
||||||
|
|
||||||
if (nElements.size > 2) return false;
|
if (nElements.size > 2) return false;
|
||||||
|
|
||||||
return !!(this.fields[Object.keys(this.fields)[0]].schema && this.fields[Object.keys(this.fields)[0]].table);
|
return !!(props.fields[Object.keys(props.fields)[0]].schema && props.fields[Object.keys(props.fields)[0]].table);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
|
||||||
isBaseSelectField () {
|
|
||||||
return this.isForeignKey(this.editingField) || this.inputProps.type === 'boolean' || this.enumArray;
|
|
||||||
},
|
|
||||||
enumArray () {
|
|
||||||
if (this.fields[this.editingField] && this.fields[this.editingField].enumValues)
|
|
||||||
return this.fields[this.editingField].enumValues.replaceAll('\'', '').split(',');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
fields () {
|
|
||||||
Object.keys(this.fields).forEach(field => {
|
|
||||||
this.isInlineEditor[field.name] = false;
|
|
||||||
});
|
});
|
||||||
},
|
|
||||||
isTextareaEditor (val) {
|
|
||||||
if (val) {
|
|
||||||
const modelOperations = new ModelOperations();
|
|
||||||
(async () => {
|
|
||||||
const detected = await modelOperations.runModel(this.editingContent);
|
|
||||||
const filteredLanguages = detected.filter(dLang =>
|
|
||||||
this.availableLanguages.some(aLang => aLang.id === dLang.languageId) &&
|
|
||||||
dLang.confidence > 0.1
|
|
||||||
);
|
|
||||||
|
|
||||||
if (filteredLanguages.length)
|
const isBaseSelectField = computed(() => {
|
||||||
this.editorMode = this.availableLanguages.find(lang => lang.id === filteredLanguages[0].languageId).slug;
|
return isForeignKey(editingField.value) || inputProps.value.type === 'boolean' || enumArray.value;
|
||||||
})();
|
});
|
||||||
}
|
|
||||||
},
|
|
||||||
selected (isSelected) {
|
|
||||||
if (isSelected)
|
|
||||||
window.addEventListener('keydown', this.onKey);
|
|
||||||
|
|
||||||
else {
|
const enumArray = computed(() => {
|
||||||
this.editOFF();
|
if (props.fields[editingField.value] && props.fields[editingField.value].enumValues)
|
||||||
window.removeEventListener('keydown', this.onKey);
|
return props.fields[editingField.value].enumValues.replaceAll('\'', '').split(',');
|
||||||
}
|
return false;
|
||||||
}
|
});
|
||||||
},
|
|
||||||
beforeUnmount () {
|
const isForeignKey = (key: string) => {
|
||||||
if (this.selected)
|
if (key) {
|
||||||
window.removeEventListener('keydown', this.onKey);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
isForeignKey (key) {
|
|
||||||
if (key.includes('.'))
|
if (key.includes('.'))
|
||||||
key = key.split('.').pop();
|
key = key.split('.').pop();
|
||||||
|
|
||||||
return this.foreignKeys.includes(key);
|
return foreignKeys.value.includes(key);
|
||||||
},
|
}
|
||||||
isNull (value) {
|
};
|
||||||
|
|
||||||
|
const isNull = (value: null | string | number) => {
|
||||||
return value === null ? ' is-null' : '';
|
return value === null ? ' is-null' : '';
|
||||||
},
|
};
|
||||||
typeClass (type) {
|
|
||||||
|
const typeClass = (type: string) => {
|
||||||
if (type)
|
if (type)
|
||||||
return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`;
|
return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`;
|
||||||
return '';
|
return '';
|
||||||
},
|
};
|
||||||
bufferToBase64 (val) {
|
|
||||||
return bufferToBase64(val);
|
|
||||||
},
|
|
||||||
editON (field) {
|
|
||||||
if (!this.isEditable || this.editingType === 'none') return;
|
|
||||||
|
|
||||||
const content = this.row[field];
|
const editON = async (field: string) => {
|
||||||
const type = this.fields[field].type.toUpperCase();
|
if (!isEditable.value || editingType.value === 'none') return;
|
||||||
this.originalContent = this.typeFormat(content, type, this.fields[field].length);
|
|
||||||
this.editingType = type;
|
const content = props.row[field];
|
||||||
this.editingField = field;
|
const type = props.fields[field].type.toUpperCase();
|
||||||
this.editingLength = this.fields[field].length;
|
originalContent.value = typeFormat(content, type, props.fields[field].length);
|
||||||
|
editingType.value = type;
|
||||||
|
editingField.value = field;
|
||||||
|
editingLength.value = props.fields[field].length;
|
||||||
|
|
||||||
if ([...LONG_TEXT, ...ARRAY, ...TEXT_SEARCH].includes(type)) {
|
if ([...LONG_TEXT, ...ARRAY, ...TEXT_SEARCH].includes(type)) {
|
||||||
this.isTextareaEditor = true;
|
isTextareaEditor.value = true;
|
||||||
this.editingContent = this.typeFormat(content, type);
|
editingContent.value = typeFormat(content, type);
|
||||||
this.$emit('start-editing', field);
|
emit('start-editing', field);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SPATIAL.includes(type)) {
|
if (SPATIAL.includes(type)) {
|
||||||
if (content) {
|
if (content) {
|
||||||
this.isMultiSpatial = IS_MULTI_SPATIAL.includes(type);
|
isMultiSpatial.value = IS_MULTI_SPATIAL.includes(type);
|
||||||
this.isMapModal = true;
|
isMapModal.value = true;
|
||||||
this.editingContent = this.typeFormat(content, type);
|
editingContent.value = typeFormat(content, type);
|
||||||
}
|
}
|
||||||
this.$emit('start-editing', field);
|
emit('start-editing', field);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BLOB.includes(type)) {
|
if (BLOB.includes(type)) {
|
||||||
this.isBlobEditor = true;
|
isBlobEditor.value = true;
|
||||||
this.editingContent = content || '';
|
editingContent.value = content || '';
|
||||||
this.fileToUpload = null;
|
fileToUpload.value = null;
|
||||||
this.willBeDeleted = false;
|
willBeDeleted.value = false;
|
||||||
|
|
||||||
if (content !== null) {
|
if (content !== null) {
|
||||||
const buff = Buffer.from(this.editingContent);
|
const buff = Buffer.from(editingContent.value);
|
||||||
if (buff.length) {
|
if (buff.length) {
|
||||||
const hex = buff.toString('hex').substring(0, 8).toUpperCase();
|
const hex = buff.toString('hex').substring(0, 8).toUpperCase();
|
||||||
const { ext, mime } = mimeFromHex(hex);
|
const { ext, mime } = mimeFromHex(hex);
|
||||||
this.contentInfo = {
|
contentInfo.value = {
|
||||||
ext,
|
ext,
|
||||||
mime,
|
mime,
|
||||||
size: this.editingContent.length
|
size: editingContent.value.length
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.$emit('start-editing', field);
|
emit('start-editing', field);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inline editable fields
|
// Inline editable fields
|
||||||
this.editingContent = this.originalContent;
|
editingContent.value = originalContent.value;
|
||||||
|
|
||||||
const obj = { [field]: true };
|
const obj = { [field]: true };
|
||||||
this.isInlineEditor = { ...this.isInlineEditor, ...obj };
|
isInlineEditor.value = { ...isInlineEditor.value, ...obj };
|
||||||
|
nextTick(() => {
|
||||||
this.$nextTick(() => { // Focus on input
|
document.querySelector<HTMLInputElement>('.editable-field').focus();
|
||||||
document.querySelector('.editable-field').focus();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$emit('start-editing', field);
|
emit('start-editing', field);
|
||||||
},
|
};
|
||||||
editOFF () {
|
|
||||||
if (!this.editingField) return;
|
|
||||||
|
|
||||||
this.isInlineEditor[this.editingField] = false;
|
const editOFF = () => {
|
||||||
|
if (!editingField.value) return;
|
||||||
|
|
||||||
|
isInlineEditor.value[editingField.value] = false;
|
||||||
let content;
|
let content;
|
||||||
if (!BLOB.includes(this.editingType)) {
|
if (!BLOB.includes(editingType.value)) {
|
||||||
if ([...DATETIME, ...TIME].includes(this.editingType)) {
|
if ([...DATETIME, ...TIME].includes(editingType.value)) {
|
||||||
if (this.editingContent.substring(this.editingContent.length - 1) === '.')
|
if (editingContent.value.substring(editingContent.value.length - 1) === '.')
|
||||||
this.editingContent = this.editingContent.slice(0, -1);
|
editingContent.value = editingContent.value.slice(0, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not changed
|
// If not changed
|
||||||
if (this.editingContent === this.typeFormat(this.originalContent, this.editingType, this.editingLength)) {
|
if (editingContent.value === typeFormat(originalContent.value, editingType.value, editingLength.value)) {
|
||||||
this.editingType = null;
|
editingType.value = null;
|
||||||
this.editingField = null;
|
editingField.value = null;
|
||||||
this.$emit('stop-editing', this.editingField);
|
emit('stop-editing', editingField.value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
content = this.editingContent;
|
content = editingContent.value;
|
||||||
}
|
}
|
||||||
else { // Handle file upload
|
else { // Handle file upload
|
||||||
if (this.willBeDeleted) {
|
if (willBeDeleted.value) {
|
||||||
content = '';
|
content = '';
|
||||||
this.willBeDeleted = false;
|
willBeDeleted.value = false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!this.fileToUpload) return;
|
if (!fileToUpload.value) return;
|
||||||
content = this.fileToUpload.file.path;
|
content = fileToUpload.value.file.path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$emit('update-field', {
|
emit('update-field', {
|
||||||
field: this.fields[this.editingField].name,
|
field: props.fields[editingField.value].name,
|
||||||
type: this.editingType,
|
type: editingType.value,
|
||||||
content
|
content
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$emit('stop-editing', this.editingField);
|
emit('stop-editing', editingField.value);
|
||||||
|
|
||||||
this.editingType = null;
|
editingType.value = null;
|
||||||
this.editingField = null;
|
editingField.value = null;
|
||||||
},
|
};
|
||||||
hideEditorModal () {
|
|
||||||
this.isTextareaEditor = false;
|
const hideEditorModal = () => {
|
||||||
this.isBlobEditor = false;
|
isTextareaEditor.value = false;
|
||||||
this.isMapModal = false;
|
isBlobEditor.value = false;
|
||||||
this.isMultiSpatial = false;
|
isMapModal.value = false;
|
||||||
this.$emit('stop-editing', this.editingField);
|
isMultiSpatial.value = false;
|
||||||
},
|
emit('stop-editing', editingField.value);
|
||||||
downloadFile () {
|
};
|
||||||
|
|
||||||
|
const downloadFile = () => {
|
||||||
const downloadLink = document.createElement('a');
|
const downloadLink = document.createElement('a');
|
||||||
|
|
||||||
downloadLink.href = `data:${this.contentInfo.mime};base64, ${bufferToBase64(this.editingContent)}`;
|
downloadLink.href = `data:${contentInfo.value.mime};base64, ${bufferToBase64(editingContent.value)}`;
|
||||||
downloadLink.setAttribute('download', `${this.editingField}.${this.contentInfo.ext}`);
|
downloadLink.setAttribute('download', `${editingField.value}.${contentInfo.value.ext}`);
|
||||||
document.body.appendChild(downloadLink);
|
document.body.appendChild(downloadLink);
|
||||||
|
|
||||||
downloadLink.click();
|
downloadLink.click();
|
||||||
downloadLink.remove();
|
downloadLink.remove();
|
||||||
},
|
};
|
||||||
filesChange (event) {
|
|
||||||
|
const filesChange = (event: Event & {target: {files: {name: string}[]}}) => {
|
||||||
const { files } = event.target;
|
const { files } = event.target;
|
||||||
if (!files.length) return;
|
if (!files.length) return;
|
||||||
|
|
||||||
this.fileToUpload = { name: files[0].name, file: files[0] };
|
fileToUpload.value = { name: files[0].name, file: files[0] };
|
||||||
this.willBeDeleted = false;
|
willBeDeleted.value = false;
|
||||||
},
|
};
|
||||||
prepareToDelete () {
|
|
||||||
this.editingContent = '';
|
const prepareToDelete = () => {
|
||||||
this.contentInfo = {
|
editingContent.value = '';
|
||||||
|
contentInfo.value = {
|
||||||
ext: '',
|
ext: '',
|
||||||
mime: '',
|
mime: '',
|
||||||
size: null
|
size: null
|
||||||
};
|
};
|
||||||
this.willBeDeleted = true;
|
willBeDeleted.value = true;
|
||||||
},
|
};
|
||||||
selectRow (event, field) {
|
|
||||||
this.$emit('select-row', event, this.row, field);
|
const selectRow = (event: Event, field: string) => {
|
||||||
},
|
emit('select-row', event, props.row, field);
|
||||||
getKeyUsage (keyName) {
|
};
|
||||||
|
|
||||||
|
const getKeyUsage = (keyName: string) => {
|
||||||
if (keyName.includes('.'))
|
if (keyName.includes('.'))
|
||||||
return this.keyUsage.find(key => key.field === keyName.split('.').pop());
|
return props.keyUsage.find(key => key.field === keyName.split('.').pop());
|
||||||
return this.keyUsage.find(key => key.field === keyName);
|
return props.keyUsage.find(key => key.field === keyName);
|
||||||
},
|
};
|
||||||
openContext (event, payload) {
|
|
||||||
payload.field = this.fields[payload.orgField].name;// Ensures field name only
|
const openContext = (event: MouseEvent, payload: { id: string; field?: string; orgField: string; isEditable?: boolean }) => {
|
||||||
payload.isEditable = this.isEditable;
|
payload.field = props.fields[payload.orgField].name;// Ensures field name only
|
||||||
this.$emit('contextmenu', event, payload);
|
payload.isEditable = isEditable.value;
|
||||||
},
|
emit('contextmenu', event, payload);
|
||||||
onKey (e) {
|
};
|
||||||
|
|
||||||
|
const onKey = (e: KeyboardEvent) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
if (!this.editingField && e.key === 'Enter')
|
if (!editingField.value && e.key === 'Enter')
|
||||||
return this.editON(this.selectedCell);
|
return editON(props.selectedCell);
|
||||||
|
|
||||||
if (this.editingField && e.key === 'Enter' && !this.isBaseSelectField)
|
if (editingField.value && e.key === 'Enter' && !isBaseSelectField.value)
|
||||||
return this.editOFF();
|
return editOFF();
|
||||||
|
|
||||||
if (this.editingField && e.key === 'Escape') {
|
if (editingField.value && e.key === 'Escape') {
|
||||||
this.isInlineEditor[this.editingField] = false;
|
isInlineEditor.value[editingField.value] = false;
|
||||||
this.editingField = null;
|
editingField.value = null;
|
||||||
this.$emit('stop-editing', this.editingField);
|
emit('stop-editing', editingField.value);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
formatBytes,
|
|
||||||
cutText (val) {
|
const cutText = (val: string) => {
|
||||||
if (typeof val !== 'string') return val;
|
if (typeof val !== 'string') return val;
|
||||||
return val.length > 128 ? `${val.substring(0, 128)}[...]` : val;
|
return val.length > 128 ? `${val.substring(0, 128)}[...]` : val;
|
||||||
},
|
};
|
||||||
typeFormat (val, type, precision) {
|
|
||||||
|
const typeFormat = (val: string | number | Date | number[], type: string, precision?: number | false) => {
|
||||||
if (!val) return val;
|
if (!val) return val;
|
||||||
|
|
||||||
type = type.toUpperCase();
|
type = type.toUpperCase();
|
||||||
@ -593,7 +570,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (BLOB.includes(type)) {
|
if (BLOB.includes(type)) {
|
||||||
const buff = Buffer.from(val);
|
const buff = Buffer.from(val as string);
|
||||||
if (!buff.length) return '';
|
if (!buff.length) return '';
|
||||||
|
|
||||||
const hex = buff.toString('hex').substring(0, 8).toUpperCase();
|
const hex = buff.toString('hex').substring(0, 8).toUpperCase();
|
||||||
@ -601,10 +578,10 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (BIT.includes(type)) {
|
if (BIT.includes(type)) {
|
||||||
if (typeof val === 'number') val = [val];
|
if (typeof val === 'number') val = [val] as number[];
|
||||||
const hex = Buffer.from(val).toString('hex');
|
const hex = Buffer.from(val as number[]).toString('hex') as unknown as HexChar[];
|
||||||
const bitString = hexToBinary(hex);
|
const bitString = hexToBinary(hex);
|
||||||
return parseInt(bitString).toString().padStart(precision, '0');
|
return parseInt(bitString).toString().padStart(Number(precision), '0');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ARRAY.includes(type)) {
|
if (ARRAY.includes(type)) {
|
||||||
@ -617,9 +594,44 @@ export default {
|
|||||||
return val;
|
return val;
|
||||||
|
|
||||||
return typeof val === 'object' ? JSON.stringify(val) : val;
|
return typeof val === 'object' ? JSON.stringify(val) : val;
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(() => props.fields, () => {
|
||||||
|
Object.keys(props.fields).forEach(field => {
|
||||||
|
isInlineEditor.value[field] = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(isTextareaEditor, (val) => {
|
||||||
|
if (val) {
|
||||||
|
const modelOperations = new ModelOperations();
|
||||||
|
(async () => {
|
||||||
|
const detected = await modelOperations.runModel(editingContent.value);
|
||||||
|
const filteredLanguages = detected.filter(dLang =>
|
||||||
|
availableLanguages.value.some(aLang => aLang.id === dLang.languageId) &&
|
||||||
|
dLang.confidence > 0.1
|
||||||
|
);
|
||||||
|
|
||||||
|
if (filteredLanguages.length)
|
||||||
|
editorMode.value = availableLanguages.value.find(lang => lang.id === filteredLanguages[0].languageId).slug;
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => props.selected, (isSelected) => {
|
||||||
|
if (isSelected)
|
||||||
|
window.addEventListener('keydown', onKey);
|
||||||
|
|
||||||
|
else {
|
||||||
|
editOFF();
|
||||||
|
window.removeEventListener('keydown', onKey);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (props.selected)
|
||||||
|
window.removeEventListener('keydown', onKey);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<button
|
<button
|
||||||
class="btn btn-dark btn-sm mr-0 pr-1"
|
class="btn btn-dark btn-sm mr-0 pr-1"
|
||||||
:class="{'loading':isQuering}"
|
:class="{'loading':isQuering}"
|
||||||
:title="`${$t('word.refresh')} (F5)`"
|
:title="`${t('word.refresh')} (F5)`"
|
||||||
@click="reloadTable"
|
@click="reloadTable"
|
||||||
>
|
>
|
||||||
<i v-if="!+autorefreshTimer" class="mdi mdi-24px mdi-refresh mr-1" />
|
<i v-if="!+autorefreshTimer" class="mdi mdi-24px mdi-refresh mr-1" />
|
||||||
@ -18,7 +18,7 @@
|
|||||||
<i class="mdi mdi-24px mdi-menu-down" />
|
<i class="mdi mdi-24px mdi-menu-down" />
|
||||||
</div>
|
</div>
|
||||||
<div class="menu px-3">
|
<div class="menu px-3">
|
||||||
<span>{{ $t('word.autoRefresh') }}: <b>{{ +autorefreshTimer ? `${autorefreshTimer}s` : 'OFF' }}</b></span>
|
<span>{{ t('word.autoRefresh') }}: <b>{{ +autorefreshTimer ? `${autorefreshTimer}s` : 'OFF' }}</b></span>
|
||||||
<input
|
<input
|
||||||
v-model="autorefreshTimer"
|
v-model="autorefreshTimer"
|
||||||
class="slider no-border"
|
class="slider no-border"
|
||||||
@ -46,7 +46,7 @@
|
|||||||
{{ page }}
|
{{ page }}
|
||||||
</div>
|
</div>
|
||||||
<div class="menu px-3">
|
<div class="menu px-3">
|
||||||
<span>{{ $t('message.pageNumber') }}</span>
|
<span>{{ t('message.pageNumber') }}</span>
|
||||||
<input
|
<input
|
||||||
ref="pageSelect"
|
ref="pageSelect"
|
||||||
v-model="pageProxy"
|
v-model="pageProxy"
|
||||||
@ -72,7 +72,7 @@
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
class="btn btn-sm"
|
class="btn btn-sm"
|
||||||
:title="`${$t('word.filter')} (CTRL+F)`"
|
:title="`${t('word.filter')} (CTRL+F)`"
|
||||||
:class="{'btn-primary': isSearch, 'btn-dark': !isSearch}"
|
:class="{'btn-primary': isSearch, 'btn-dark': !isSearch}"
|
||||||
@click="isSearch = !isSearch"
|
@click="isSearch = !isSearch"
|
||||||
>
|
>
|
||||||
@ -95,7 +95,7 @@
|
|||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-file-export mr-1" />
|
<i class="mdi mdi-24px mdi-file-export mr-1" />
|
||||||
<span>{{ $t('word.export') }}</span>
|
<span>{{ t('word.export') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-menu-down" />
|
<i class="mdi mdi-24px mdi-menu-down" />
|
||||||
</button>
|
</button>
|
||||||
<ul class="menu text-left">
|
<ul class="menu text-left">
|
||||||
@ -112,22 +112,22 @@
|
|||||||
<div
|
<div
|
||||||
v-if="results.length"
|
v-if="results.length"
|
||||||
class="d-flex"
|
class="d-flex"
|
||||||
:title="$t('message.queryDuration')"
|
:title="t('message.queryDuration')"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-timer-sand mdi-rotate-180 pr-1" /> <b>{{ results[0].duration / 1000 }}s</b>
|
<i class="mdi mdi-timer-sand mdi-rotate-180 pr-1" /> <b>{{ results[0].duration / 1000 }}s</b>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="results.length && results[0].rows">
|
<div v-if="results.length && results[0].rows">
|
||||||
{{ $t('word.results') }}: <b>{{ localeString(results[0].rows.length) }}</b>
|
{{ t('word.results') }}: <b>{{ localeString(results[0].rows.length) }}</b>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="hasApproximately || (page > 1 && approximateCount)">
|
<div v-if="hasApproximately || (page > 1 && approximateCount)">
|
||||||
{{ $t('word.total') }}: <b
|
{{ t('word.total') }}: <b
|
||||||
:title="!customizations.tableRealCount ? $t('word.approximately') : ''"
|
:title="!customizations.tableRealCount ? t('word.approximately') : ''"
|
||||||
>
|
>
|
||||||
<span v-if="!customizations.tableRealCount">≈</span>
|
<span v-if="!customizations.tableRealCount">≈</span>
|
||||||
{{ localeString(approximateCount) }}
|
{{ localeString(approximateCount) }}
|
||||||
</b>
|
</b>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex" :title="$t('word.schema')">
|
<div class="d-flex" :title="t('word.schema')">
|
||||||
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
|
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -167,291 +167,288 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
|
import { computed, onBeforeUnmount, Prop, ref, Ref, watch } from 'vue';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import Tables from '@/ipc-api/Tables';
|
import Tables from '@/ipc-api/Tables';
|
||||||
|
import { useResultTables } from '@/composables/useResultTables';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
import { useNotificationsStore } from '@/stores/notifications';
|
||||||
import { useSettingsStore } from '@/stores/settings';
|
import { useSettingsStore } from '@/stores/settings';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
import BaseLoader from '@/components/BaseLoader';
|
import BaseLoader from '@/components/BaseLoader.vue';
|
||||||
import WorkspaceTabQueryTable from '@/components/WorkspaceTabQueryTable';
|
import WorkspaceTabQueryTable from '@/components/WorkspaceTabQueryTable.vue';
|
||||||
import WorkspaceTabTableFilters from '@/components/WorkspaceTabTableFilters';
|
import WorkspaceTabTableFilters from '@/components/WorkspaceTabTableFilters.vue';
|
||||||
import ModalFakerRows from '@/components/ModalFakerRows';
|
import ModalFakerRows from '@/components/ModalFakerRows.vue';
|
||||||
import tableTabs from '@/mixins/tableTabs';
|
import { ConnectionParams } from 'common/interfaces/antares';
|
||||||
|
import { TableFilterClausole } from 'common/interfaces/tableApis';
|
||||||
|
|
||||||
export default {
|
const { t } = useI18n();
|
||||||
name: 'WorkspaceTabTable',
|
|
||||||
components: {
|
const props = defineProps({
|
||||||
BaseLoader,
|
connection: Object as Prop<ConnectionParams>,
|
||||||
WorkspaceTabQueryTable,
|
|
||||||
WorkspaceTabTableFilters,
|
|
||||||
ModalFakerRows
|
|
||||||
},
|
|
||||||
mixins: [tableTabs],
|
|
||||||
props: {
|
|
||||||
connection: Object,
|
|
||||||
isSelected: Boolean,
|
isSelected: Boolean,
|
||||||
table: String,
|
table: String,
|
||||||
schema: String,
|
schema: String,
|
||||||
elementType: String
|
elementType: String
|
||||||
},
|
});
|
||||||
setup () {
|
|
||||||
|
const reloadTable = () => getTableData();
|
||||||
|
|
||||||
|
const {
|
||||||
|
queryTable,
|
||||||
|
isQuering,
|
||||||
|
updateField,
|
||||||
|
deleteSelected
|
||||||
|
} = useResultTables(props.connection.uid, reloadTable);
|
||||||
|
|
||||||
const { addNotification } = useNotificationsStore();
|
const { addNotification } = useNotificationsStore();
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
const workspacesStore = useWorkspacesStore();
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
|
||||||
const { dataTabLimit: limit } = storeToRefs(settingsStore);
|
const { dataTabLimit: limit } = storeToRefs(settingsStore);
|
||||||
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
|
|
||||||
|
|
||||||
const { changeBreadcrumbs, getWorkspace } = workspacesStore;
|
const { changeBreadcrumbs, getWorkspace } = workspacesStore;
|
||||||
|
|
||||||
return {
|
const pageSelect: Ref<HTMLInputElement> = ref(null);
|
||||||
addNotification,
|
const tabUid = ref('data');
|
||||||
limit,
|
const isPageMenu = ref(false);
|
||||||
selectedWorkspace,
|
const isSearch = ref(false);
|
||||||
changeBreadcrumbs,
|
const results = ref([]);
|
||||||
getWorkspace
|
const lastTable = ref(null);
|
||||||
};
|
const isFakerModal = ref(false);
|
||||||
},
|
const autorefreshTimer = ref(0);
|
||||||
data () {
|
const refreshInterval = ref(null);
|
||||||
return {
|
const sortParams = ref({} as { field: string; dir: 'asc' | 'desc'});
|
||||||
tabUid: 'data', // ???
|
const filters = ref([]);
|
||||||
isQuering: false,
|
const page = ref(1);
|
||||||
isPageMenu: false,
|
const pageProxy = ref(1);
|
||||||
isSearch: false,
|
const approximateCount = ref(0);
|
||||||
results: [],
|
|
||||||
lastTable: null,
|
|
||||||
isFakerModal: false,
|
|
||||||
autorefreshTimer: 0,
|
|
||||||
refreshInterval: null,
|
|
||||||
sortParams: {},
|
|
||||||
filters: [],
|
|
||||||
page: 1,
|
|
||||||
pageProxy: 1,
|
|
||||||
approximateCount: 0
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
workspace () {
|
|
||||||
return this.getWorkspace(this.connection.uid);
|
|
||||||
},
|
|
||||||
customizations () {
|
|
||||||
return this.workspace.customizations;
|
|
||||||
},
|
|
||||||
isTable () {
|
|
||||||
return !!this.workspace.breadcrumbs.table;
|
|
||||||
},
|
|
||||||
fields () {
|
|
||||||
return this.results.length ? this.results[0].fields : [];
|
|
||||||
},
|
|
||||||
keyUsage () {
|
|
||||||
return this.results.length ? this.results[0].keys : [];
|
|
||||||
},
|
|
||||||
tableInfo () {
|
|
||||||
try {
|
|
||||||
return this.workspace.structure.find(db => db.name === this.schema).tables.find(table => table.name === this.table);
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
return { rows: 0 };
|
|
||||||
}
|
|
||||||
},
|
|
||||||
hasApproximately () {
|
|
||||||
return this.results.length &&
|
|
||||||
this.results[0].rows &&
|
|
||||||
this.results[0].rows.length === this.limit &&
|
|
||||||
this.results[0].rows.length < this.approximateCount;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
schema () {
|
|
||||||
if (this.isSelected) {
|
|
||||||
this.page = 1;
|
|
||||||
this.approximateCount = 0;
|
|
||||||
this.sortParams = {};
|
|
||||||
this.getTableData();
|
|
||||||
this.lastTable = this.table;
|
|
||||||
this.$refs.queryTable.resetSort();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
table () {
|
|
||||||
if (this.isSelected) {
|
|
||||||
this.page = 1;
|
|
||||||
this.approximateCount = 0;
|
|
||||||
this.sortParams = {};
|
|
||||||
this.getTableData();
|
|
||||||
this.lastTable = this.table;
|
|
||||||
this.$refs.queryTable.resetSort();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
page (val, oldVal) {
|
|
||||||
if (val && val > 0 && val !== oldVal) {
|
|
||||||
this.pageProxy = this.page;
|
|
||||||
this.getTableData();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
isSelected (val) {
|
|
||||||
if (val) {
|
|
||||||
this.changeBreadcrumbs({ schema: this.schema, [this.elementType]: this.table });
|
|
||||||
|
|
||||||
if (this.lastTable !== this.table)
|
const workspace = computed(() => {
|
||||||
this.getTableData();
|
return getWorkspace(props.connection.uid);
|
||||||
}
|
});
|
||||||
},
|
|
||||||
isSearch (val) {
|
const customizations = computed(() => {
|
||||||
if (this.filters.length > 0 && !val) {
|
return workspace.value.customizations;
|
||||||
this.filters = [];
|
});
|
||||||
this.getTableData();
|
|
||||||
}
|
const isTable = computed(() => {
|
||||||
this.resizeScroller();
|
return !!workspace.value.breadcrumbs.table;
|
||||||
}
|
});
|
||||||
},
|
|
||||||
created () {
|
const fields = computed(() => {
|
||||||
this.getTableData();
|
return results.value.length ? results.value[0].fields : [];
|
||||||
window.addEventListener('keydown', this.onKey);
|
});
|
||||||
},
|
|
||||||
beforeUnmount () {
|
const keyUsage = computed(() => {
|
||||||
window.removeEventListener('keydown', this.onKey);
|
return results.value.length ? results.value[0].keys : [];
|
||||||
clearInterval(this.refreshInterval);
|
});
|
||||||
},
|
|
||||||
methods: {
|
const getTableData = async () => {
|
||||||
async getTableData () {
|
if (!props.table || !props.isSelected) return;
|
||||||
if (!this.table || !this.isSelected) return;
|
isQuering.value = true;
|
||||||
this.isQuering = true;
|
|
||||||
|
|
||||||
// if table changes clear cached values
|
// if table changes clear cached values
|
||||||
if (this.lastTable !== this.table)
|
if (lastTable.value !== props.table)
|
||||||
this.results = [];
|
results.value = [];
|
||||||
|
|
||||||
this.lastTable = this.table;
|
lastTable.value = props.table;
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: props.connection.uid,
|
||||||
schema: this.schema,
|
schema: props.schema,
|
||||||
table: this.table,
|
table: props.table,
|
||||||
limit: this.limit,
|
limit: limit.value,
|
||||||
page: this.page,
|
page: page.value,
|
||||||
sortParams: { ...this.sortParams },
|
sortParams: { ...sortParams.value },
|
||||||
where: [...this.filters] || []
|
where: [...filters.value] || []
|
||||||
};
|
};
|
||||||
|
|
||||||
try { // Table data
|
try { // Table data
|
||||||
const { status, response } = await Tables.getTableData(params);
|
const { status, response } = await Tables.getTableData(params);
|
||||||
|
|
||||||
if (status === 'success')
|
if (status === 'success')
|
||||||
this.results = [response];
|
results.value = [response];
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
addNotification({ status: 'error', message: response });
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.results.length && this.results[0].rows.length === this.limit) {
|
if (results.value.length && results.value[0].rows.length === limit.value) {
|
||||||
try { // Table approximate count
|
try { // Table approximate count
|
||||||
const { status, response } = await Tables.getTableApproximateCount(params);
|
const { status, response } = await Tables.getTableApproximateCount(params);
|
||||||
|
|
||||||
if (status === 'success')
|
if (status === 'success')
|
||||||
this.approximateCount = response;
|
approximateCount.value = response;
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
addNotification({ status: 'error', message: response as unknown as string });
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isQuering = false;
|
isQuering.value = false;
|
||||||
},
|
};
|
||||||
getTable () {
|
|
||||||
return this.table;
|
|
||||||
},
|
|
||||||
reloadTable () {
|
|
||||||
this.getTableData();
|
|
||||||
},
|
|
||||||
hardSort (sortParams) {
|
|
||||||
this.sortParams = sortParams;
|
|
||||||
this.getTableData();
|
|
||||||
},
|
|
||||||
openPageMenu () {
|
|
||||||
if (this.isQuering || (this.results.length && this.results[0].rows.length < this.limit && this.page === 1)) return;
|
|
||||||
|
|
||||||
this.isPageMenu = true;
|
const hardSort = (params: { field: string; dir: 'asc' | 'desc'}) => {
|
||||||
if (this.isPageMenu)
|
sortParams.value = params;
|
||||||
setTimeout(() => this.$refs.pageSelect.focus(), 20);
|
getTableData();
|
||||||
},
|
};
|
||||||
setPageNumber () {
|
|
||||||
this.isPageMenu = false;
|
|
||||||
|
|
||||||
if (this.pageProxy > 0)
|
const openPageMenu = () => {
|
||||||
this.page = this.pageProxy;
|
if (isQuering.value || (results.value.length && results.value[0].rows.length < limit.value && page.value === 1)) return;
|
||||||
|
|
||||||
|
isPageMenu.value = true;
|
||||||
|
if (isPageMenu.value)
|
||||||
|
setTimeout(() => pageSelect.value.focus(), 20);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setPageNumber = () => {
|
||||||
|
isPageMenu.value = false;
|
||||||
|
|
||||||
|
if (pageProxy.value > 0)
|
||||||
|
page.value = pageProxy.value;
|
||||||
else
|
else
|
||||||
this.pageProxy = this.page;
|
pageProxy.value = page.value;
|
||||||
},
|
};
|
||||||
pageChange (direction) {
|
|
||||||
if (this.isQuering) return;
|
|
||||||
|
|
||||||
if (direction === 'next' && (this.results.length && this.results[0].rows.length === this.limit)) {
|
const pageChange = (direction: 'prev' | 'next') => {
|
||||||
if (!this.page)
|
if (isQuering.value) return;
|
||||||
this.page = 2;
|
|
||||||
|
if (direction === 'next' && (results.value.length && results.value[0].rows.length === limit.value)) {
|
||||||
|
if (!page.value)
|
||||||
|
page.value = 2;
|
||||||
else
|
else
|
||||||
this.page++;
|
page.value++;
|
||||||
}
|
}
|
||||||
else if (direction === 'prev' && this.page > 1)
|
else if (direction === 'prev' && page.value > 1)
|
||||||
this.page--;
|
page.value--;
|
||||||
},
|
};
|
||||||
showFakerModal () {
|
|
||||||
if (this.isQuering) return;
|
const showFakerModal = () => {
|
||||||
this.isFakerModal = true;
|
if (isQuering.value) return;
|
||||||
},
|
isFakerModal.value = true;
|
||||||
hideFakerModal () {
|
};
|
||||||
this.isFakerModal = false;
|
|
||||||
},
|
const hideFakerModal = () => {
|
||||||
onKey (e) {
|
isFakerModal.value = false;
|
||||||
if (this.isSelected) {
|
};
|
||||||
|
|
||||||
|
const onKey = (e: KeyboardEvent) => {
|
||||||
|
if (props.isSelected) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (e.key === 'F5')
|
if (e.key === 'F5')
|
||||||
this.reloadTable();
|
reloadTable();
|
||||||
|
|
||||||
if (e.ctrlKey || e.metaKey) {
|
if (e.ctrlKey || e.metaKey) {
|
||||||
if (e.key === 'ArrowRight')
|
if (e.key === 'ArrowRight')
|
||||||
this.pageChange('next');
|
pageChange('next');
|
||||||
if (e.key === 'ArrowLeft')
|
if (e.key === 'ArrowLeft')
|
||||||
this.pageChange('prev');
|
pageChange('prev');
|
||||||
if (e.keyCode === 70) // f
|
if (e.key === 'f') // f
|
||||||
this.isSearch = !this.isSearch;
|
isSearch.value = !isSearch.value;
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setRefreshInterval () {
|
|
||||||
if (this.refreshInterval)
|
|
||||||
clearInterval(this.refreshInterval);
|
|
||||||
|
|
||||||
if (+this.autorefreshTimer) {
|
|
||||||
this.refreshInterval = setInterval(() => {
|
|
||||||
if (!this.isQuering)
|
|
||||||
this.reloadTable();
|
|
||||||
}, this.autorefreshTimer * 1000);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
downloadTable (format) {
|
|
||||||
this.$refs.queryTable.downloadTable(format, this.table);
|
|
||||||
},
|
|
||||||
onFilterChange (clausoles) {
|
|
||||||
this.resizeScroller();
|
|
||||||
if (clausoles.length === 0)
|
|
||||||
this.isSearch = false;
|
|
||||||
},
|
|
||||||
resizeScroller () {
|
|
||||||
setTimeout(() => this.$refs.queryTable.refreshScroller(), 1);
|
|
||||||
},
|
|
||||||
updateFilters (clausoles) {
|
|
||||||
this.filters = clausoles;
|
|
||||||
this.getTableData();
|
|
||||||
},
|
|
||||||
localeString (val) {
|
|
||||||
if (val !== null)
|
|
||||||
return val.toLocaleString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const setRefreshInterval = () => {
|
||||||
|
if (refreshInterval.value)
|
||||||
|
clearInterval(refreshInterval.value);
|
||||||
|
|
||||||
|
if (+autorefreshTimer.value) {
|
||||||
|
refreshInterval.value = setInterval(() => {
|
||||||
|
if (!isQuering.value)
|
||||||
|
reloadTable();
|
||||||
|
}, autorefreshTimer.value * 1000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadTable = (format: 'csv' | 'json') => {
|
||||||
|
queryTable.value.downloadTable(format, props.table);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onFilterChange = (clausoles: TableFilterClausole[]) => {
|
||||||
|
resizeScroller();
|
||||||
|
if (clausoles.length === 0)
|
||||||
|
isSearch.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const resizeScroller = () => {
|
||||||
|
setTimeout(() => queryTable.value.refreshScroller(), 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateFilters = (clausoles: TableFilterClausole[]) => {
|
||||||
|
filters.value = clausoles;
|
||||||
|
getTableData();
|
||||||
|
};
|
||||||
|
|
||||||
|
const localeString = (val: number | null) => {
|
||||||
|
if (val !== null)
|
||||||
|
return val.toLocaleString();
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasApproximately = computed(() => {
|
||||||
|
return results.value.length &&
|
||||||
|
results.value[0].rows &&
|
||||||
|
results.value[0].rows.length === limit.value &&
|
||||||
|
results.value[0].rows.length < approximateCount.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => props.schema, () => {
|
||||||
|
if (props.isSelected) {
|
||||||
|
page.value = 1;
|
||||||
|
approximateCount.value = 0;
|
||||||
|
sortParams.value = {} as { field: string; dir: 'asc' | 'desc'};
|
||||||
|
getTableData();
|
||||||
|
lastTable.value = props.table;
|
||||||
|
queryTable.value.resetSort();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => props.table, () => {
|
||||||
|
if (props.isSelected) {
|
||||||
|
page.value = 1;
|
||||||
|
approximateCount.value = 0;
|
||||||
|
sortParams.value = {} as { field: string; dir: 'asc' | 'desc'};
|
||||||
|
getTableData();
|
||||||
|
lastTable.value = props.table;
|
||||||
|
queryTable.value.resetSort();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(page, (val, oldVal) => {
|
||||||
|
if (val && val > 0 && val !== oldVal) {
|
||||||
|
pageProxy.value = page.value;
|
||||||
|
getTableData();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => props.isSelected, (val) => {
|
||||||
|
if (val) {
|
||||||
|
changeBreadcrumbs({ schema: props.schema, [props.elementType]: props.table });
|
||||||
|
|
||||||
|
if (lastTable.value !== props.table)
|
||||||
|
getTableData();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(isSearch, (val) => {
|
||||||
|
if (filters.value.length > 0 && !val) {
|
||||||
|
filters.value = [];
|
||||||
|
getTableData();
|
||||||
|
}
|
||||||
|
resizeScroller();
|
||||||
|
});
|
||||||
|
|
||||||
|
getTableData();
|
||||||
|
window.addEventListener('keydown', onKey);
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.removeEventListener('keydown', onKey);
|
||||||
|
clearInterval(refreshInterval.value);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -64,53 +64,47 @@
|
|||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
|
import { computed, Prop, ref } from 'vue';
|
||||||
|
import { ClientCode, TableField } from 'common/interfaces/antares';
|
||||||
import customizations from 'common/customizations';
|
import customizations from 'common/customizations';
|
||||||
import { NUMBER, FLOAT } from 'common/fieldTypes';
|
import { NUMBER, FLOAT } from 'common/fieldTypes';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
import { TableFilterClausole } from 'common/interfaces/tableApis';
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
components: {
|
fields: Array as Prop<TableField[]>,
|
||||||
BaseSelect
|
connClient: String as Prop<ClientCode>
|
||||||
},
|
});
|
||||||
props: {
|
|
||||||
fields: Array,
|
const emit = defineEmits(['filter-change', 'filter']);
|
||||||
connClient: String
|
|
||||||
},
|
const rows = ref([]);
|
||||||
emits: ['filter-change', 'filter'],
|
const operators = ref([
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
rows: [],
|
|
||||||
operators: [
|
|
||||||
'=', '!=', '>', '<', '>=', '<=', 'IN', 'NOT IN', 'LIKE', 'BETWEEN', 'IS NULL', 'IS NOT NULL'
|
'=', '!=', '>', '<', '>=', '<=', 'IN', 'NOT IN', 'LIKE', 'BETWEEN', 'IS NULL', 'IS NOT NULL'
|
||||||
]
|
]);
|
||||||
|
|
||||||
|
const clientCustomizations = computed(() => customizations[props.connClient]);
|
||||||
|
|
||||||
|
const addRow = () => {
|
||||||
|
rows.value.push({ active: true, field: props.fields[0].name, op: '=', value: '', value2: '' });
|
||||||
|
emit('filter-change', rows.value);
|
||||||
};
|
};
|
||||||
},
|
|
||||||
computed: {
|
const removeRow = (i: number) => {
|
||||||
customizations () {
|
rows.value = rows.value.filter((_, idx) => idx !== i);
|
||||||
return customizations[this.connClient];
|
emit('filter-change', rows.value);
|
||||||
}
|
};
|
||||||
},
|
|
||||||
created () {
|
const doFilter = () => {
|
||||||
this.addRow();
|
const clausoles = rows.value.filter(el => el.active).map(el => createClausole(el));
|
||||||
},
|
emit('filter', clausoles);
|
||||||
methods: {
|
};
|
||||||
addRow () {
|
|
||||||
this.rows.push({ active: true, field: this.fields[0].name, op: '=', value: '', value2: '' });
|
const createClausole = (filter: TableFilterClausole) => {
|
||||||
this.$emit('filter-change', this.rows);
|
const field = props.fields.find(field => field.name === filter.field);
|
||||||
},
|
|
||||||
removeRow (i) {
|
|
||||||
this.rows = this.rows.filter((_, idx) => idx !== i);
|
|
||||||
this.$emit('filter-change', this.rows);
|
|
||||||
},
|
|
||||||
doFilter () {
|
|
||||||
const clausoles = this.rows.filter(el => el.active).map(el => this.createClausole(el));
|
|
||||||
this.$emit('filter', clausoles);
|
|
||||||
},
|
|
||||||
createClausole (filter) {
|
|
||||||
const field = this.fields.find(field => field.name === filter.field);
|
|
||||||
const isNumeric = [...NUMBER, ...FLOAT].includes(field.type);
|
const isNumeric = [...NUMBER, ...FLOAT].includes(field.type);
|
||||||
const { elementsWrapper: ew, stringsWrapper: sw } = this.customizations;
|
const { elementsWrapper: ew, stringsWrapper: sw } = clientCustomizations.value;
|
||||||
let value;
|
let value;
|
||||||
|
|
||||||
switch (filter.op) {
|
switch (filter.op) {
|
||||||
@ -146,9 +140,9 @@ export default {
|
|||||||
value = `${sw}${sw}`;
|
value = `${sw}${sw}`;
|
||||||
|
|
||||||
return `${ew}${filter.field}${ew} ${filter.op} ${value}`;
|
return `${ew}${filter.field}${ew} ${filter.op} ${value}`;
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
addRow();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
@ -1,12 +1,20 @@
|
|||||||
// TODO: unfinished
|
|
||||||
import Tables from '@/ipc-api/Tables';
|
import Tables from '@/ipc-api/Tables';
|
||||||
import { ref } from 'vue';
|
import { Component, Ref, ref } from 'vue';
|
||||||
|
import { useNotificationsStore } from '@/stores/notifications';
|
||||||
|
import { TableDeleteParams, TableUpdateParams } from 'common/interfaces/tableApis';
|
||||||
|
const { addNotification } = useNotificationsStore();
|
||||||
|
|
||||||
export default function useResultTables (uid, reloadTable, addNotification) {
|
export function useResultTables (uid: string, reloadTable: () => void) {
|
||||||
const tableRef = ref(null);
|
const queryTable: Ref<Component & {
|
||||||
|
resetSort: () => void;
|
||||||
|
resizeResults: () => void;
|
||||||
|
refreshScroller: () => void;
|
||||||
|
downloadTable: (format: string, fileName: string) => void;
|
||||||
|
applyUpdate: (payload: TableUpdateParams) => void;
|
||||||
|
}> = ref(null);
|
||||||
const isQuering = ref(false);
|
const isQuering = ref(false);
|
||||||
|
|
||||||
async function updateField (payload) {
|
async function updateField (payload: TableUpdateParams) {
|
||||||
isQuering.value = true;
|
isQuering.value = true;
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
@ -20,7 +28,7 @@ export default function useResultTables (uid, reloadTable, addNotification) {
|
|||||||
if (response.reload)// Needed for blob fields
|
if (response.reload)// Needed for blob fields
|
||||||
reloadTable();
|
reloadTable();
|
||||||
else
|
else
|
||||||
tableRef.value.applyUpdate(payload);
|
queryTable.value.applyUpdate(payload);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
addNotification({ status: 'error', message: response });
|
addNotification({ status: 'error', message: response });
|
||||||
@ -32,7 +40,7 @@ export default function useResultTables (uid, reloadTable, addNotification) {
|
|||||||
isQuering.value = false;
|
isQuering.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteSelected (payload) {
|
async function deleteSelected (payload: TableDeleteParams) {
|
||||||
isQuering.value = true;
|
isQuering.value = true;
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
@ -56,7 +64,7 @@ export default function useResultTables (uid, reloadTable, addNotification) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tableRef,
|
queryTable,
|
||||||
isQuering,
|
isQuering,
|
||||||
updateField,
|
updateField,
|
||||||
deleteSelected
|
deleteSelected
|
@ -15,7 +15,7 @@ export default class {
|
|||||||
return ipcRenderer.invoke('alter-function', unproxify(params));
|
return ipcRenderer.invoke('alter-function', unproxify(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
static alterTriggerFunction (params: {func: CreateFunctionParams & { uid: string }}): Promise<IpcResponse> {
|
static alterTriggerFunction (params: { uid: string; func: AlterFunctionParams }): Promise<IpcResponse> {
|
||||||
return ipcRenderer.invoke('alter-trigger-function', unproxify(params));
|
return ipcRenderer.invoke('alter-trigger-function', unproxify(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ export default class {
|
|||||||
return ipcRenderer.invoke('drop-routine', unproxify(params));
|
return ipcRenderer.invoke('drop-routine', unproxify(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
static alterRoutine (params: { routine: AlterRoutineParams & { uid: string } }): Promise<IpcResponse> {
|
static alterRoutine (params: { uid: string; routine: AlterRoutineParams }): Promise<IpcResponse> {
|
||||||
return ipcRenderer.invoke('alter-routine', unproxify(params));
|
return ipcRenderer.invoke('alter-routine', unproxify(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
import { unproxify } from '../libs/unproxify';
|
import { unproxify } from '../libs/unproxify';
|
||||||
import { AlterEventParams, CreateEventParams, EventInfos, IpcResponse } from 'common/interfaces/antares';
|
import { AlterEventParams, CreateEventParams, IpcResponse } from 'common/interfaces/antares';
|
||||||
|
|
||||||
export default class {
|
export default class {
|
||||||
static getSchedulerInformations (params: { uid: string; schema: string; scheduler: string}): Promise<IpcResponse<EventInfos>> {
|
static getSchedulerInformations (params: { uid: string; schema: string; scheduler: string}): Promise<IpcResponse> {
|
||||||
return ipcRenderer.invoke('get-scheduler-informations', unproxify(params));
|
return ipcRenderer.invoke('get-scheduler-informations', unproxify(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -11,7 +11,7 @@ export default class {
|
|||||||
return ipcRenderer.invoke('drop-scheduler', unproxify(params));
|
return ipcRenderer.invoke('drop-scheduler', unproxify(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
static alterScheduler (params: { scheduler: AlterEventParams & { uid: string } }): Promise<IpcResponse> {
|
static alterScheduler (params: { uid: string; scheduler: AlterEventParams }): Promise<IpcResponse> {
|
||||||
return ipcRenderer.invoke('alter-scheduler', unproxify(params));
|
return ipcRenderer.invoke('alter-scheduler', unproxify(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ export default class {
|
|||||||
return ipcRenderer.invoke('get-processes', uid);
|
return ipcRenderer.invoke('get-processes', uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
static killProcess (params: { uid: string; pid: string }): Promise<IpcResponse> {
|
static killProcess (params: { uid: string; pid: number }): Promise<IpcResponse> {
|
||||||
return ipcRenderer.invoke('kill-process', unproxify(params));
|
return ipcRenderer.invoke('kill-process', unproxify(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
import { unproxify } from '../libs/unproxify';
|
import { unproxify } from '../libs/unproxify';
|
||||||
import { AlterTableParams, CreateTableParams, IpcResponse, TableForeign, TableIndex, TableInfos } from 'common/interfaces/antares';
|
import { AlterTableParams, CreateTableParams, IpcResponse } from 'common/interfaces/antares';
|
||||||
|
|
||||||
export default class {
|
export default class {
|
||||||
static getTableColumns (params: {schema: string; table: string }): Promise<IpcResponse> {
|
static getTableColumns (params: {schema: string; table: string }): Promise<IpcResponse> {
|
||||||
@ -27,15 +27,15 @@ export default class {
|
|||||||
return ipcRenderer.invoke('get-table-count', unproxify(params));
|
return ipcRenderer.invoke('get-table-count', unproxify(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
static getTableOptions (params: { uid: string; schema: string; table: string }): Promise<IpcResponse<TableInfos>> {
|
static getTableOptions (params: { uid: string; schema: string; table: string }): Promise<IpcResponse> {
|
||||||
return ipcRenderer.invoke('get-table-options', unproxify(params));
|
return ipcRenderer.invoke('get-table-options', unproxify(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
static getTableIndexes (params: { uid: string; schema: string; table: string }): Promise<IpcResponse<TableIndex[]>> {
|
static getTableIndexes (params: { uid: string; schema: string; table: string }): Promise<IpcResponse> {
|
||||||
return ipcRenderer.invoke('get-table-indexes', unproxify(params));
|
return ipcRenderer.invoke('get-table-indexes', unproxify(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
static getKeyUsage (params: { uid: string; schema: string; table: string }): Promise<IpcResponse<TableForeign[]>> {
|
static getKeyUsage (params: { uid: string; schema: string; table: string }): Promise<IpcResponse> {
|
||||||
return ipcRenderer.invoke('get-key-usage', unproxify(params));
|
return ipcRenderer.invoke('get-key-usage', unproxify(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
import Tables from '@/ipc-api/Tables';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
methods: {
|
|
||||||
async updateField (payload) {
|
|
||||||
this.isQuering = true;
|
|
||||||
|
|
||||||
const params = {
|
|
||||||
uid: this.connection.uid,
|
|
||||||
...payload
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { status, response } = await Tables.updateTableCell(params);
|
|
||||||
if (status === 'success') {
|
|
||||||
if (response.reload)// Needed for blob fields
|
|
||||||
this.reloadTable();
|
|
||||||
else
|
|
||||||
this.$refs.queryTable.applyUpdate(payload);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
this.addNotification({ status: 'error', message: response });
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
|
||||||
}
|
|
||||||
|
|
||||||
this.isQuering = false;
|
|
||||||
},
|
|
||||||
async deleteSelected (payload) {
|
|
||||||
this.isQuering = true;
|
|
||||||
|
|
||||||
const params = {
|
|
||||||
uid: this.connection.uid,
|
|
||||||
...payload
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { status, response } = await Tables.deleteTableRows(params);
|
|
||||||
this.isQuering = false;
|
|
||||||
|
|
||||||
if (status === 'success')
|
|
||||||
this.reloadTable();
|
|
||||||
else
|
|
||||||
this.addNotification({ status: 'error', message: response });
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
|
||||||
this.isQuering = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
Reference in New Issue
Block a user