mirror of
https://github.com/Fabio286/antares.git
synced 2025-06-05 21:59:22 +02:00
feat: triggers edit
This commit is contained in:
@ -77,6 +77,12 @@
|
||||
:connection="connection"
|
||||
:view="workspace.breadcrumbs.view"
|
||||
/>
|
||||
<WorkspacePropsTabTrigger
|
||||
v-show="selectedTab === 'prop' && workspace.breadcrumbs.trigger"
|
||||
:is-selected="selectedTab === 'prop'"
|
||||
:connection="connection"
|
||||
:trigger="workspace.breadcrumbs.trigger"
|
||||
/>
|
||||
<WorkspaceTableTab
|
||||
v-show="selectedTab === 'data'"
|
||||
:connection="connection"
|
||||
@ -101,6 +107,7 @@ import WorkspaceQueryTab from '@/components/WorkspaceQueryTab';
|
||||
import WorkspaceTableTab from '@/components/WorkspaceTableTab';
|
||||
import WorkspacePropsTab from '@/components/WorkspacePropsTab';
|
||||
import WorkspacePropsTabView from '@/components/WorkspacePropsTabView';
|
||||
import WorkspacePropsTabTrigger from '@/components/WorkspacePropsTabTrigger';
|
||||
|
||||
export default {
|
||||
name: 'Workspace',
|
||||
@ -109,7 +116,8 @@ export default {
|
||||
WorkspaceQueryTab,
|
||||
WorkspaceTableTab,
|
||||
WorkspacePropsTab,
|
||||
WorkspacePropsTabView
|
||||
WorkspacePropsTabView,
|
||||
WorkspacePropsTabTrigger
|
||||
},
|
||||
props: {
|
||||
connection: Object
|
||||
@ -131,7 +139,14 @@ export default {
|
||||
return this.selectedWorkspace === this.connection.uid;
|
||||
},
|
||||
selectedTab () {
|
||||
if (this.workspace.breadcrumbs.table === null && this.workspace.breadcrumbs.view === null && ['data', 'prop'].includes(this.workspace.selected_tab))
|
||||
if (
|
||||
this.workspace.breadcrumbs.table === null &&
|
||||
this.workspace.breadcrumbs.view === null &&
|
||||
this.workspace.breadcrumbs.trigger === null &&
|
||||
this.workspace.breadcrumbs.procedure === null &&
|
||||
this.workspace.breadcrumbs.scheduler === null &&
|
||||
['data', 'prop'].includes(this.workspace.selected_tab)
|
||||
)
|
||||
return this.queryTabs[0].uid;
|
||||
|
||||
return this.queryTabs.find(tab => tab.uid === this.workspace.selected_tab) ||
|
||||
|
@ -35,6 +35,90 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div v-if="database.triggers.length" class="database-misc">
|
||||
<details class="accordion">
|
||||
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.trigger}">
|
||||
<i class="misc-icon mdi mdi-18px mdi-folder-cog mr-1" />
|
||||
{{ $tc('word.trigger', 2) }}
|
||||
</summary>
|
||||
<div class="accordion-body">
|
||||
<div>
|
||||
<ul class="menu menu-nav pt-0">
|
||||
<li
|
||||
v-for="trigger of database.triggers"
|
||||
:key="trigger.name"
|
||||
class="menu-item"
|
||||
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.trigger === trigger.name}"
|
||||
@click="setBreadcrumbs({schema: database.name, trigger: trigger.name})"
|
||||
@contextmenu.prevent="showTableContext($event, trigger)"
|
||||
>
|
||||
<a class="table-name">
|
||||
<i class="table-icon mdi mdi-table-cog mdi-18px mr-1" />
|
||||
<span>{{ trigger.name }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<div v-if="database.procedures.length" class="database-misc">
|
||||
<details class="accordion">
|
||||
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.procedure}">
|
||||
<i class="misc-icon mdi mdi-18px mdi-folder-move mr-1" />
|
||||
{{ $tc('word.storedRoutine', 2) }}
|
||||
</summary>
|
||||
<div class="accordion-body">
|
||||
<div>
|
||||
<ul class="menu menu-nav pt-0">
|
||||
<li
|
||||
v-for="procedure of database.procedures"
|
||||
:key="procedure.name"
|
||||
class="menu-item"
|
||||
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.procedure === procedure.name}"
|
||||
@click="setBreadcrumbs({schema: database.name, procedure: procedure.name})"
|
||||
@contextmenu.prevent="showTableContext($event, procedure)"
|
||||
>
|
||||
<a class="table-name">
|
||||
<i class="table-icon mdi mdi-arrow-right-bold-box mdi-18px mr-1" />
|
||||
<span>{{ procedure.name }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<div v-if="database.schedulers.length" class="database-misc">
|
||||
<details class="accordion">
|
||||
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.scheduler}">
|
||||
<i class="misc-icon mdi mdi-18px mdi-folder-clock mr-1" />
|
||||
{{ $tc('word.scheduler', 2) }}
|
||||
</summary>
|
||||
<div class="accordion-body">
|
||||
<div>
|
||||
<ul class="menu menu-nav pt-0">
|
||||
<li
|
||||
v-for="scheduler of database.schedulers"
|
||||
:key="scheduler.name"
|
||||
class="menu-item"
|
||||
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.scheduler === scheduler.name}"
|
||||
@click="setBreadcrumbs({schema: database.name, scheduler: scheduler.name})"
|
||||
@contextmenu.prevent="showTableContext($event, scheduler)"
|
||||
>
|
||||
<a class="table-name">
|
||||
<i class="table-icon mdi mdi-calendar-clock mdi-18px mr-1" />
|
||||
<span>{{ scheduler.name }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</template>
|
||||
@ -72,9 +156,11 @@ export default {
|
||||
}),
|
||||
formatBytes,
|
||||
showDatabaseContext (event, database) {
|
||||
this.changeBreadcrumbs({ schema: database, table: null });
|
||||
this.$emit('show-database-context', { event, database });
|
||||
},
|
||||
showTableContext (event, table) {
|
||||
this.setBreadcrumbs({ schema: this.database.name, [table.type]: table.name });
|
||||
this.$emit('show-table-context', { event, table });
|
||||
},
|
||||
piePercentage (val) {
|
||||
@ -92,6 +178,7 @@ export default {
|
||||
<style lang="scss">
|
||||
.workspace-explorebar-database {
|
||||
.database-name,
|
||||
.misc-name,
|
||||
a.table-name {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -107,12 +194,20 @@ export default {
|
||||
}
|
||||
|
||||
.database-icon,
|
||||
.table-icon {
|
||||
.table-icon,
|
||||
.misc-icon {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
.database-name {
|
||||
.misc-name {
|
||||
line-height: 1;
|
||||
padding: 0.1rem 1rem 0.1rem 0.1rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.database-name,
|
||||
.misc-name {
|
||||
&:hover {
|
||||
color: $body-font-color;
|
||||
background: rgba($color: #fff, $alpha: 0.05);
|
||||
@ -142,6 +237,18 @@ export default {
|
||||
margin-left: 1.2rem;
|
||||
}
|
||||
|
||||
.database-misc {
|
||||
margin-left: 1.6rem;
|
||||
|
||||
.accordion[open] .accordion-header > .misc-icon:first-child::before {
|
||||
content: "\F0770";
|
||||
}
|
||||
|
||||
.accordion-body {
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.table-size {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
|
@ -13,14 +13,14 @@
|
||||
<div class="context-element" @click="showCreateViewModal">
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-table-eye text-light pr-1" /> {{ $t('word.view') }}</span>
|
||||
</div>
|
||||
<div class="context-element d-none" @click="false">
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-table-cog text-light pr-1" /> {{ $t('word.trigger') }}</span>
|
||||
<div class="context-element" @click="false">
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-table-cog text-light pr-1" /> {{ $tc('word.trigger', 1) }}</span>
|
||||
</div>
|
||||
<div class="context-element d-none" @click="false">
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-cog-box pr-1" /> {{ $t('word.storedRoutine') }}</span>
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-arrow-right-bold-box pr-1" /> {{ $tc('word.storedRoutine', 1) }}</span>
|
||||
</div>
|
||||
<div class="context-element d-none" @click="false">
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-calendar-clock text-light pr-1" /> {{ $t('word.scheduler') }}</span>
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-calendar-clock text-light pr-1" /> {{ $tc('word.scheduler', 1) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
275
src/renderer/components/WorkspacePropsTabTrigger.vue
Normal file
275
src/renderer/components/WorkspacePropsTabTrigger.vue
Normal file
@ -0,0 +1,275 @@
|
||||
<template>
|
||||
<div class="workspace-query-tab column col-12 columns col-gapless">
|
||||
<div class="workspace-query-runner column col-12">
|
||||
<div class="workspace-query-runner-footer">
|
||||
<div class="workspace-query-buttons">
|
||||
<button
|
||||
class="btn btn-primary btn-sm"
|
||||
:disabled="!isChanged"
|
||||
:class="{'loading':isSaving}"
|
||||
@click="saveChanges"
|
||||
>
|
||||
<span>{{ $t('word.save') }}</span>
|
||||
<i class="mdi mdi-24px mdi-content-save ml-1" />
|
||||
</button>
|
||||
<button
|
||||
:disabled="!isChanged"
|
||||
class="btn btn-link btn-sm mr-0"
|
||||
:title="$t('message.clearChanges')"
|
||||
@click="clearChanges"
|
||||
>
|
||||
<span>{{ $t('word.clear') }}</span>
|
||||
<i class="mdi mdi-24px mdi-delete-sweep ml-1" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="columns mb-4">
|
||||
<div class="column col-3">
|
||||
<div class="form-group">
|
||||
<label class="form-label">{{ $t('word.name') }}</label>
|
||||
<input
|
||||
v-model="localTrigger.name"
|
||||
class="form-input"
|
||||
type="text"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column col-3">
|
||||
<div class="form-group">
|
||||
<label class="form-label">{{ $t('word.definer') }}</label>
|
||||
<select
|
||||
v-if="workspace.users.length"
|
||||
v-model="localTrigger.definer"
|
||||
class="form-select"
|
||||
>
|
||||
<option value="">
|
||||
{{ $t('message.currentUser') }}
|
||||
</option>
|
||||
<option v-if="!isDefinerInUsers" :value="originalTrigger.definer">
|
||||
{{ originalTrigger.definer.replaceAll('`', '') }}
|
||||
</option>
|
||||
<option
|
||||
v-for="user in workspace.users"
|
||||
:key="`${user.name}@${user.host}`"
|
||||
:value="`\`${user.name}\`@\`${user.host}\``"
|
||||
>
|
||||
{{ user.name }}@{{ user.host }}
|
||||
</option>
|
||||
</select>
|
||||
<select v-if="!workspace.users.length" class="form-select">
|
||||
<option value="">
|
||||
{{ $t('message.currentUser') }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column col-3">
|
||||
<div class="form-group">
|
||||
<label class="form-label">{{ $t('word.table') }}</label>
|
||||
<select v-model="localTrigger.table" class="form-select">
|
||||
<option v-for="table in schemaTables" :key="table.name">
|
||||
{{ table.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column col-3">
|
||||
<div class="form-group">
|
||||
<label class="form-label">{{ $t('word.event') }}</label>
|
||||
<div class="input-group">
|
||||
<select v-model="localTrigger.event1" class="form-select">
|
||||
<option>BEFORE</option>
|
||||
<option>AFTER</option>
|
||||
</select>
|
||||
<select v-model="localTrigger.event2" class="form-select">
|
||||
<option>INSERT</option>
|
||||
<option>UPDATE</option>
|
||||
<option>DELETE</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="workspace-query-results column col-12 mt-2">
|
||||
<label class="form-label ml-2">{{ $t('message.triggerStatement') }}</label>
|
||||
<QueryEditor
|
||||
v-if="isSelected"
|
||||
ref="queryEditor"
|
||||
:value.sync="localTrigger.sql"
|
||||
:workspace="workspace"
|
||||
:schema="schema"
|
||||
:height="editorHeight"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import QueryEditor from '@/components/QueryEditor';
|
||||
import Triggers from '@/ipc-api/Triggers';
|
||||
|
||||
export default {
|
||||
name: 'WorkspacePropsTabTrigger',
|
||||
components: {
|
||||
QueryEditor
|
||||
},
|
||||
props: {
|
||||
connection: Object,
|
||||
trigger: String
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
tabUid: 'prop',
|
||||
isQuering: false,
|
||||
isSaving: false,
|
||||
originalTrigger: null,
|
||||
localTrigger: { sql: '' },
|
||||
lastTrigger: null,
|
||||
sqlProxy: '',
|
||||
editorHeight: 300
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
getWorkspace: 'workspaces/getWorkspace'
|
||||
}),
|
||||
workspace () {
|
||||
return this.getWorkspace(this.connection.uid);
|
||||
},
|
||||
isSelected () {
|
||||
return this.workspace.selected_tab === 'prop';
|
||||
},
|
||||
schema () {
|
||||
return this.workspace.breadcrumbs.schema;
|
||||
},
|
||||
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);
|
||||
|
||||
return schemaTables.length ? schemaTables[0].filter(table => table.type === 'table') : [];
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
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.lastTrigger !== this.trigger) {
|
||||
await this.getTriggerData();
|
||||
this.$refs.queryEditor.editor.session.setValue(this.localTrigger.sql);
|
||||
this.lastTrigger = this.trigger;
|
||||
}
|
||||
},
|
||||
isChanged (val) {
|
||||
if (this.isSelected && this.lastTrigger === this.trigger && this.trigger !== null)
|
||||
this.setUnsavedChanges(val);
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
window.addEventListener('resize', this.resizeQueryEditor);
|
||||
},
|
||||
destroyed () {
|
||||
window.removeEventListener('resize', this.resizeQueryEditor);
|
||||
},
|
||||
methods: {
|
||||
...mapActions({
|
||||
addNotification: 'notifications/addNotification',
|
||||
refreshStructure: 'workspaces/refreshStructure',
|
||||
setUnsavedChanges: 'workspaces/setUnsavedChanges',
|
||||
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
||||
}),
|
||||
async getTriggerData () {
|
||||
if (!this.trigger) return;
|
||||
this.isQuering = true;
|
||||
|
||||
const params = {
|
||||
uid: this.connection.uid,
|
||||
schema: this.schema,
|
||||
trigger: this.workspace.breadcrumbs.trigger
|
||||
};
|
||||
|
||||
try {
|
||||
const { status, response } = await Triggers.getTriggerInformations(params);
|
||||
if (status === 'success') {
|
||||
this.originalTrigger = response;
|
||||
this.localTrigger = JSON.parse(JSON.stringify(this.originalTrigger));
|
||||
this.sqlProxy = this.localTrigger.sql;
|
||||
}
|
||||
else
|
||||
this.addNotification({ status: 'error', message: response });
|
||||
}
|
||||
catch (err) {
|
||||
this.addNotification({ status: 'error', message: err.stack });
|
||||
}
|
||||
|
||||
this.resizeQueryEditor();
|
||||
this.isQuering = false;
|
||||
},
|
||||
async saveChanges () {
|
||||
if (this.isSaving) return;
|
||||
this.isSaving = true;
|
||||
const params = {
|
||||
uid: this.connection.uid,
|
||||
schema: this.schema,
|
||||
trigger: {
|
||||
...this.localTrigger,
|
||||
oldName: this.originalTrigger.name
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const { status, response } = await Triggers.alterTrigger(params);
|
||||
|
||||
if (status === 'success') {
|
||||
const oldName = this.originalTrigger.name;
|
||||
|
||||
await this.refreshStructure(this.connection.uid);
|
||||
|
||||
if (oldName !== this.localTrigger.name) {
|
||||
this.setUnsavedChanges(false);
|
||||
this.changeBreadcrumbs({ schema: this.schema, trigger: this.localTrigger.name });
|
||||
}
|
||||
|
||||
this.getTriggerData();
|
||||
}
|
||||
else
|
||||
this.addNotification({ status: 'error', message: response });
|
||||
}
|
||||
catch (err) {
|
||||
this.addNotification({ status: 'error', message: err.stack });
|
||||
}
|
||||
|
||||
this.isSaving = false;
|
||||
},
|
||||
clearChanges () {
|
||||
this.localTrigger = JSON.parse(JSON.stringify(this.originalTrigger));
|
||||
this.$refs.queryEditor.editor.session.setValue(this.localTrigger.sql);
|
||||
},
|
||||
resizeQueryEditor () {
|
||||
if (this.$refs.queryEditor) {
|
||||
const footer = document.getElementById('footer');
|
||||
const size = window.innerHeight - this.$refs.queryEditor.$el.getBoundingClientRect().top - footer.offsetHeight;
|
||||
this.editorHeight = size;
|
||||
this.$refs.queryEditor.editor.resize();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
@ -47,6 +47,9 @@
|
||||
<option value="">
|
||||
{{ $t('message.currentUser') }}
|
||||
</option>
|
||||
<option v-if="!isDefinerInUsers" :value="originalView.definer">
|
||||
{{ originalView.definer.replaceAll('`', '') }}
|
||||
</option>
|
||||
<option
|
||||
v-for="user in workspace.users"
|
||||
:key="`${user.name}@${user.host}`"
|
||||
@ -208,6 +211,9 @@ export default {
|
||||
},
|
||||
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;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
Reference in New Issue
Block a user