mirror of https://github.com/Fabio286/antares.git
feat: processes list tool
This commit is contained in:
parent
db4430609e
commit
049143d143
|
@ -105,6 +105,17 @@ export default connections => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('get-processes', async (event, uid) => {
|
||||||
|
try {
|
||||||
|
const result = await connections[uid].getProcesses();
|
||||||
|
|
||||||
|
return { status: 'success', response: result };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
ipcMain.handle('use-schema', async (event, { uid, schema }) => {
|
ipcMain.handle('use-schema', async (event, { uid, schema }) => {
|
||||||
if (!schema) return;
|
if (!schema) return;
|
||||||
|
|
||||||
|
|
|
@ -956,6 +956,25 @@ export class MySQLClient extends AntaresCore {
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getProcesses () {
|
||||||
|
const sql = 'SELECT `ID`, `USER`, `HOST`, `DB`, `COMMAND`, `TIME`, `STATE`, LEFT(`INFO`, 51200) AS `INFO` FROM `information_schema`.`PROCESSLIST`';
|
||||||
|
|
||||||
|
const { rows } = await this.raw(sql);
|
||||||
|
|
||||||
|
return rows.map(row => {
|
||||||
|
return {
|
||||||
|
id: row.ID,
|
||||||
|
user: row.USER,
|
||||||
|
host: row.HOST,
|
||||||
|
db: row.DB,
|
||||||
|
command: row.COMMAND,
|
||||||
|
time: row.TIME,
|
||||||
|
state: row.STATE,
|
||||||
|
info: row.INFO
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CREATE TABLE
|
* CREATE TABLE
|
||||||
*
|
*
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
<slot name="body" />
|
<slot name="body" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer pt-0">
|
<div class="modal-footer">
|
||||||
<button
|
<button
|
||||||
class="btn btn-primary mr-2"
|
class="btn btn-primary mr-2"
|
||||||
@click.stop="confirmModal"
|
@click.stop="confirmModal"
|
||||||
|
|
|
@ -0,0 +1,307 @@
|
||||||
|
<template>
|
||||||
|
<div class="modal active">
|
||||||
|
<a class="modal-overlay" @click.stop="closeModal" />
|
||||||
|
<div class="modal-container p-0">
|
||||||
|
<div class="modal-header pl-2">
|
||||||
|
<div class="modal-title h6">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-memory mr-1" /> {{ $t('message.processesList') }}: {{ connectionName }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||||
|
</div>
|
||||||
|
<div class="processes-toolbar py-2 px-4">
|
||||||
|
<div class="dropdown">
|
||||||
|
<div class="btn-group">
|
||||||
|
<button
|
||||||
|
class="btn btn-dark btn-sm mr-0 pr-1 d-flex"
|
||||||
|
:class="{'loading':isQuering}"
|
||||||
|
title="F5"
|
||||||
|
@click="getProcessesList"
|
||||||
|
>
|
||||||
|
<span>{{ $t('word.refresh') }}</span>
|
||||||
|
<i v-if="!+autorefreshTimer" class="mdi mdi-24px mdi-refresh ml-1" />
|
||||||
|
<i v-else class="mdi mdi-24px mdi-history mdi-flip-h ml-1" />
|
||||||
|
</button>
|
||||||
|
<div class="btn btn-dark btn-sm dropdown-toggle pl-0 pr-0" tabindex="0">
|
||||||
|
<i class="mdi mdi-24px mdi-menu-down" />
|
||||||
|
</div>
|
||||||
|
<div class="menu px-3">
|
||||||
|
<span>{{ $t('word.autoRefresh') }}: <b>{{ +autorefreshTimer ? `${autorefreshTimer}s` : 'OFF' }}</b></span>
|
||||||
|
<input
|
||||||
|
v-model="autorefreshTimer"
|
||||||
|
class="slider no-border"
|
||||||
|
type="range"
|
||||||
|
min="0"
|
||||||
|
max="30"
|
||||||
|
step="1"
|
||||||
|
@change="setRefreshInterval"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="workspace-query-info">
|
||||||
|
<div v-if="sortedResults.length">
|
||||||
|
{{ $t('word.results') }}: <b>{{ sortedResults.length.toLocaleString() }}</b>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div ref="tableWrapper" class="modal-body py-0">
|
||||||
|
<div class="vscroll workspace-query-results" :style="{'height': resultsSize+'px'}">
|
||||||
|
<div ref="table" class="table table-hover">
|
||||||
|
<div class="thead">
|
||||||
|
<div class="tr">
|
||||||
|
<div
|
||||||
|
v-for="(field, index) in fields"
|
||||||
|
:key="index"
|
||||||
|
class="th c-hand"
|
||||||
|
>
|
||||||
|
<div ref="columnResize" class="column-resizable">
|
||||||
|
<div class="table-column-title" @click="sort(field)">
|
||||||
|
<span>{{ field.toUpperCase() }}</span>
|
||||||
|
<i
|
||||||
|
v-if="currentSort === field"
|
||||||
|
class="mdi sort-icon"
|
||||||
|
:class="currentSortDir === 'asc' ? 'mdi-sort-ascending':'mdi-sort-descending'"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<BaseVirtualScroll
|
||||||
|
ref="resultTable"
|
||||||
|
:items="sortedResults"
|
||||||
|
:item-height="22"
|
||||||
|
class="tbody"
|
||||||
|
:visible-height="resultsSize"
|
||||||
|
:scroll-element="scrollElement"
|
||||||
|
>
|
||||||
|
<template slot-scope="{ items }">
|
||||||
|
<ProcessesListRow
|
||||||
|
v-for="row in items"
|
||||||
|
:key="row._id"
|
||||||
|
class="process-row"
|
||||||
|
:row="row"
|
||||||
|
@contextmenu="contextMenu"
|
||||||
|
@show-info="showInfoModal"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</BaseVirtualScroll>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer text-light">
|
||||||
|
<button class="btn btn-link" @click.stop="closeModal">
|
||||||
|
{{ $t('word.close') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
|
import Database from '@/ipc-api/Database';
|
||||||
|
import BaseVirtualScroll from '@/components/BaseVirtualScroll';
|
||||||
|
import ProcessesListRow from '@/components/ProcessesListRow';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ModalProcessesList',
|
||||||
|
components: {
|
||||||
|
BaseVirtualScroll,
|
||||||
|
ProcessesListRow
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
connection: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
resultsSize: 1000,
|
||||||
|
isQuering: false,
|
||||||
|
autorefreshTimer: 0,
|
||||||
|
refreshInterval: null,
|
||||||
|
results: [],
|
||||||
|
fields: [],
|
||||||
|
currentSort: '',
|
||||||
|
currentSortDir: 'asc',
|
||||||
|
scrollElement: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
getConnectionName: 'connections/getConnectionName'
|
||||||
|
}),
|
||||||
|
connectionName () {
|
||||||
|
return this.getConnectionName(this.connection.uid);
|
||||||
|
},
|
||||||
|
sortedResults () {
|
||||||
|
if (this.currentSort) {
|
||||||
|
return [...this.results].sort((a, b) => {
|
||||||
|
let modifier = 1;
|
||||||
|
const valA = typeof a[this.currentSort] === 'string' ? a[this.currentSort].toLowerCase() : a[this.currentSort];
|
||||||
|
const valB = typeof b[this.currentSort] === 'string' ? b[this.currentSort].toLowerCase() : b[this.currentSort];
|
||||||
|
if (this.currentSortDir === 'desc') modifier = -1;
|
||||||
|
if (valA < valB) return -1 * modifier;
|
||||||
|
if (valA > valB) return 1 * modifier;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return this.results;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
window.addEventListener('keydown', this.onKey, { capture: true });
|
||||||
|
},
|
||||||
|
updated () {
|
||||||
|
if (this.$refs.table)
|
||||||
|
this.refreshScroller();
|
||||||
|
|
||||||
|
if (this.$refs.tableWrapper)
|
||||||
|
this.scrollElement = this.$refs.tableWrapper;
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.getProcessesList();
|
||||||
|
window.addEventListener('resize', this.resizeResults);
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
window.removeEventListener('keydown', this.onKey, { capture: true });
|
||||||
|
window.removeEventListener('resize', this.resizeResults);
|
||||||
|
clearInterval(this.refreshInterval);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
addNotification: 'notifications/addNotification'
|
||||||
|
}),
|
||||||
|
async getProcessesList () {
|
||||||
|
this.isQuering = true;
|
||||||
|
|
||||||
|
// if table changes clear cached values
|
||||||
|
if (this.lastTable !== this.table)
|
||||||
|
this.results = [];
|
||||||
|
|
||||||
|
try { // Table data
|
||||||
|
const { status, response } = await Database.getProcesses(this.connection.uid);
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
this.results = response;
|
||||||
|
this.fields = response.length ? Object.keys(response[0]) : [];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isQuering = false;
|
||||||
|
},
|
||||||
|
setRefreshInterval () {
|
||||||
|
this.clearRefresh();
|
||||||
|
|
||||||
|
if (+this.autorefreshTimer) {
|
||||||
|
this.refreshInterval = setInterval(() => {
|
||||||
|
if (!this.isQuering)
|
||||||
|
this.getProcessesList();
|
||||||
|
}, this.autorefreshTimer * 1000);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clearRefresh () {
|
||||||
|
if (this.refreshInterval)
|
||||||
|
clearInterval(this.refreshInterval);
|
||||||
|
},
|
||||||
|
resizeResults () {
|
||||||
|
if (this.$refs.resultTable) {
|
||||||
|
const el = this.$refs.tableWrapper;
|
||||||
|
|
||||||
|
if (el) {
|
||||||
|
const size = el.offsetHeight;
|
||||||
|
this.resultsSize = size;
|
||||||
|
}
|
||||||
|
this.$refs.resultTable.updateWindow();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
refreshScroller () {
|
||||||
|
this.resizeResults();
|
||||||
|
},
|
||||||
|
sort (field) {
|
||||||
|
if (field === this.currentSort) {
|
||||||
|
if (this.currentSortDir === 'asc')
|
||||||
|
this.currentSortDir = 'desc';
|
||||||
|
else
|
||||||
|
this.resetSort();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.currentSortDir = 'asc';
|
||||||
|
this.currentSort = field;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resetSort () {
|
||||||
|
this.currentSort = '';
|
||||||
|
this.currentSortDir = 'asc';
|
||||||
|
},
|
||||||
|
showInfoModal () {
|
||||||
|
this.autorefreshTimer = 0;
|
||||||
|
this.clearRefresh();
|
||||||
|
},
|
||||||
|
contextMenu () {},
|
||||||
|
closeModal () {
|
||||||
|
this.$emit('close');
|
||||||
|
},
|
||||||
|
onKey (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.key === 'Escape')
|
||||||
|
this.closeModal();
|
||||||
|
if (e.key === 'F5')
|
||||||
|
this.getProcessesList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.vscroll {
|
||||||
|
height: 1000px;
|
||||||
|
overflow: auto;
|
||||||
|
overflow-anchor: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-resizable {
|
||||||
|
&:hover,
|
||||||
|
&:active {
|
||||||
|
resize: horizontal;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-column-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sort-icon {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
line-height: 1;
|
||||||
|
margin-left: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-tabs {
|
||||||
|
background: transparent !important;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal {
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
.modal-container {
|
||||||
|
max-width: 75vw;
|
||||||
|
margin-top: 10vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.processes-toolbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,148 @@
|
||||||
|
<template>
|
||||||
|
<div class="tr" @click="selectRow($event, row._id)">
|
||||||
|
<div
|
||||||
|
v-for="(col, cKey) in row"
|
||||||
|
v-show="cKey !== '_id'"
|
||||||
|
:key="cKey"
|
||||||
|
class="td p-0"
|
||||||
|
tabindex="0"
|
||||||
|
@contextmenu.prevent="openContext($event, { id: row._id, field: cKey })"
|
||||||
|
>
|
||||||
|
<template v-if="cKey !== '_id'">
|
||||||
|
<span
|
||||||
|
v-if="!isInlineEditor[cKey]"
|
||||||
|
class="cell-content px-2"
|
||||||
|
:class="`${isNull(col)} type-${typeof col === 'number' ? 'int' : 'varchar'}`"
|
||||||
|
@dblclick="dblClick(cKey)"
|
||||||
|
>{{ col | cutText }}</span>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<ConfirmModal
|
||||||
|
v-if="isTextareaEditor"
|
||||||
|
:confirm-text="$t('word.update')"
|
||||||
|
size="medium"
|
||||||
|
@hide="hideEditorModal"
|
||||||
|
>
|
||||||
|
<template :slot="'header'">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-playlist-edit mr-1" /> {{ $t('word.edit') }} "{{ editingField }}"
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div :slot="'body'">
|
||||||
|
<div class="mb-2">
|
||||||
|
<div>
|
||||||
|
<TextEditor
|
||||||
|
:value.sync="editingContent"
|
||||||
|
editor-class="textarea-editor"
|
||||||
|
:mode="editorMode"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
import TextEditor from '@/components/BaseTextEditor';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ProcessesListRow',
|
||||||
|
components: {
|
||||||
|
ConfirmModal,
|
||||||
|
TextEditor
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
cutText (val) {
|
||||||
|
if (typeof val !== 'string') return val;
|
||||||
|
return val.length > 128 ? `${val.substring(0, 128)}[...]` : val;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
row: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
isInlineEditor: {},
|
||||||
|
isTextareaEditor: false,
|
||||||
|
editorMode: 'sql'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {},
|
||||||
|
watch: {
|
||||||
|
fields () {
|
||||||
|
Object.keys(this.fields).forEach(field => {
|
||||||
|
this.isInlineEditor[field.name] = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isNull (value) {
|
||||||
|
return value === null ? ' is-null' : '';
|
||||||
|
},
|
||||||
|
selectRow (event, row) {
|
||||||
|
this.$emit('select-row', event, row);
|
||||||
|
},
|
||||||
|
openContext (event, payload) {
|
||||||
|
if (this.isEditable) {
|
||||||
|
payload.field = this.fields[payload.field].name;// Ensures field name only
|
||||||
|
this.$emit('contextmenu', event, payload);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dblClick (col) {
|
||||||
|
if (col !== 'info') return;
|
||||||
|
this.$emit('show-info', col);
|
||||||
|
},
|
||||||
|
onKey (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
this.isInlineEditor[this.editingField] = false;
|
||||||
|
this.editingField = null;
|
||||||
|
window.removeEventListener('keydown', this.onKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.editable-field {
|
||||||
|
margin: 0;
|
||||||
|
border: none;
|
||||||
|
line-height: 1;
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-content {
|
||||||
|
display: block;
|
||||||
|
min-height: 0.8rem;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textarea-editor {
|
||||||
|
height: 50vh !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-field-info {
|
||||||
|
margin-top: 0.4rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-buttons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -7,10 +7,34 @@
|
||||||
ref="tabWrap"
|
ref="tabWrap"
|
||||||
class="tab tab-block column col-12"
|
class="tab tab-block column col-12"
|
||||||
>
|
>
|
||||||
<li class="tab-item d-none">
|
<li class="tab-item dropdown tools-dropdown">
|
||||||
<a class="tab-link workspace-tools-link">
|
<a
|
||||||
|
class="tab-link workspace-tools-link dropdown-toggle"
|
||||||
|
tabindex="0"
|
||||||
|
:title="$t('word.tools')"
|
||||||
|
>
|
||||||
<i class="mdi mdi-24px mdi-tools" />
|
<i class="mdi mdi-24px mdi-tools" />
|
||||||
</a>
|
</a>
|
||||||
|
<ul class="menu text-left text-uppercase">
|
||||||
|
<li class="menu-item">
|
||||||
|
<a class="c-hand p-vcentered" @click="showProcessesModal">
|
||||||
|
<i class="mdi mdi-memory mr-1 tool-icon" />
|
||||||
|
<span>{{ $t('message.processesList') }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="menu-item" title="Coming...">
|
||||||
|
<a class="c-hand p-vcentered disabled">
|
||||||
|
<i class="mdi mdi-shape mr-1 tool-icon" />
|
||||||
|
<span>{{ $t('word.variables') }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="menu-item" title="Coming...">
|
||||||
|
<a class="c-hand p-vcentered disabled">
|
||||||
|
<i class="mdi mdi-account-group mr-1 tool-icon" />
|
||||||
|
<span>{{ $t('message.manageUsers') }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
v-if="schemaChild"
|
v-if="schemaChild"
|
||||||
|
@ -19,7 +43,7 @@
|
||||||
@click="selectTab({uid: workspace.uid, tab: 'prop'})"
|
@click="selectTab({uid: workspace.uid, tab: 'prop'})"
|
||||||
>
|
>
|
||||||
<a class="tab-link">
|
<a class="tab-link">
|
||||||
<i class="mdi mdi-18px mdi-tune mr-1" />
|
<i class="mdi mdi-18px mdi-tune-vertical-variant mr-1" />
|
||||||
<span :title="schemaChild">{{ $t('word.settings').toUpperCase() }}: {{ schemaChild }}</span>
|
<span :title="schemaChild">{{ $t('word.settings').toUpperCase() }}: {{ schemaChild }}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -114,6 +138,11 @@
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<ModalProcessesList
|
||||||
|
v-if="isProcessesModal"
|
||||||
|
:connection="connection"
|
||||||
|
@close="hideProcessesModal"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -129,6 +158,7 @@ import WorkspacePropsTabTrigger from '@/components/WorkspacePropsTabTrigger';
|
||||||
import WorkspacePropsTabRoutine from '@/components/WorkspacePropsTabRoutine';
|
import WorkspacePropsTabRoutine from '@/components/WorkspacePropsTabRoutine';
|
||||||
import WorkspacePropsTabFunction from '@/components/WorkspacePropsTabFunction';
|
import WorkspacePropsTabFunction from '@/components/WorkspacePropsTabFunction';
|
||||||
import WorkspacePropsTabScheduler from '@/components/WorkspacePropsTabScheduler';
|
import WorkspacePropsTabScheduler from '@/components/WorkspacePropsTabScheduler';
|
||||||
|
import ModalProcessesList from '@/components/ModalProcessesList';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Workspace',
|
name: 'Workspace',
|
||||||
|
@ -141,14 +171,16 @@ export default {
|
||||||
WorkspacePropsTabTrigger,
|
WorkspacePropsTabTrigger,
|
||||||
WorkspacePropsTabRoutine,
|
WorkspacePropsTabRoutine,
|
||||||
WorkspacePropsTabFunction,
|
WorkspacePropsTabFunction,
|
||||||
WorkspacePropsTabScheduler
|
WorkspacePropsTabScheduler,
|
||||||
|
ModalProcessesList
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
connection: Object
|
connection: Object
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
hasWheelEvent: false
|
hasWheelEvent: false,
|
||||||
|
isProcessesModal: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -234,6 +266,12 @@ export default {
|
||||||
closeTab (tUid) {
|
closeTab (tUid) {
|
||||||
if (this.queryTabs.length === 1) return;
|
if (this.queryTabs.length === 1) return;
|
||||||
this.removeTab({ uid: this.connection.uid, tab: tUid });
|
this.removeTab({ uid: this.connection.uid, tab: tUid });
|
||||||
|
},
|
||||||
|
showProcessesModal () {
|
||||||
|
this.isProcessesModal = true;
|
||||||
|
},
|
||||||
|
hideProcessesModal () {
|
||||||
|
this.isProcessesModal = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -298,6 +336,48 @@ export default {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.tools-dropdown {
|
||||||
|
.tab-link:focus {
|
||||||
|
color: $primary-color;
|
||||||
|
opacity: 1;
|
||||||
|
outline: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu {
|
||||||
|
min-width: 100%;
|
||||||
|
|
||||||
|
.menu-item a {
|
||||||
|
border-radius: 0.1rem;
|
||||||
|
color: inherit;
|
||||||
|
display: block;
|
||||||
|
margin: 0 -0.4rem;
|
||||||
|
padding: 0.2rem 0.4rem;
|
||||||
|
text-decoration: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
border: 0;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $primary-color;
|
||||||
|
background: $bg-color-gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-icon {
|
||||||
|
line-height: 1;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
z-index: 9;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.tools-dropdown + .tab-item {
|
||||||
|
margin-left: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
.workspace-tools-link {
|
.workspace-tools-link {
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
padding-top: 0.3rem;
|
padding-top: 0.3rem;
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
/>
|
/>
|
||||||
<span>{{ field.alias || field.name }}</span>
|
<span>{{ field.alias || field.name }}</span>
|
||||||
<i
|
<i
|
||||||
v-if="idSortable && currentSort === field.name || currentSort === `${field.table}.${field.name}`"
|
v-if="isSortable && currentSort === field.name || currentSort === `${field.table}.${field.name}`"
|
||||||
class="mdi sort-icon"
|
class="mdi sort-icon"
|
||||||
:class="currentSortDir === 'asc' ? 'mdi-sort-ascending':'mdi-sort-descending'"
|
:class="currentSortDir === 'asc' ? 'mdi-sort-ascending':'mdi-sort-descending'"
|
||||||
/>
|
/>
|
||||||
|
@ -67,7 +67,6 @@
|
||||||
:row="row"
|
:row="row"
|
||||||
:fields="fieldsObj"
|
:fields="fieldsObj"
|
||||||
:key-usage="keyUsage"
|
:key-usage="keyUsage"
|
||||||
class="tr"
|
|
||||||
:class="{'selected': selectedRows.includes(row._id)}"
|
:class="{'selected': selectedRows.includes(row._id)}"
|
||||||
@select-row="selectRow($event, row._id)"
|
@select-row="selectRow($event, row._id)"
|
||||||
@update-field="updateField($event, row)"
|
@update-field="updateField($event, row)"
|
||||||
|
@ -97,7 +96,6 @@ export default {
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
results: Array,
|
results: Array,
|
||||||
tabUid: [String, Number],
|
|
||||||
connUid: String,
|
connUid: String,
|
||||||
mode: String
|
mode: String
|
||||||
},
|
},
|
||||||
|
@ -125,7 +123,7 @@ export default {
|
||||||
primaryField () {
|
primaryField () {
|
||||||
return this.fields.filter(field => ['pri', 'uni'].includes(field.key))[0] || false;
|
return this.fields.filter(field => ['pri', 'uni'].includes(field.key))[0] || false;
|
||||||
},
|
},
|
||||||
idSortable () {
|
isSortable () {
|
||||||
return this.fields.every(field => field.name);
|
return this.fields.every(field => field.name);
|
||||||
},
|
},
|
||||||
isHardSort () {
|
isHardSort () {
|
||||||
|
@ -380,7 +378,7 @@ export default {
|
||||||
this.isContext = true;
|
this.isContext = true;
|
||||||
},
|
},
|
||||||
sort (field) {
|
sort (field) {
|
||||||
if (!this.idSortable) return;
|
if (!this.isSortable) return;
|
||||||
|
|
||||||
if (this.mode === 'query')
|
if (this.mode === 'query')
|
||||||
field = `${this.getTable(this.resultsetIndex)}.${field}`;
|
field = `${this.getTable(this.resultsetIndex)}.${field}`;
|
||||||
|
|
|
@ -97,7 +97,9 @@ module.exports = {
|
||||||
content: 'Content',
|
content: 'Content',
|
||||||
cut: 'Cut',
|
cut: 'Cut',
|
||||||
copy: 'Copy',
|
copy: 'Copy',
|
||||||
paste: 'Paste'
|
paste: 'Paste',
|
||||||
|
tools: 'Tools',
|
||||||
|
variables: 'Variables'
|
||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
appWelcome: 'Welcome to Antares SQL Client!',
|
appWelcome: 'Welcome to Antares SQL Client!',
|
||||||
|
@ -193,7 +195,9 @@ module.exports = {
|
||||||
selectAll: 'Select all',
|
selectAll: 'Select all',
|
||||||
queryDuration: 'Query duration',
|
queryDuration: 'Query duration',
|
||||||
includeBetaUpdates: 'Include beta updates',
|
includeBetaUpdates: 'Include beta updates',
|
||||||
setNull: 'Set NULL'
|
setNull: 'Set NULL',
|
||||||
|
processesList: 'Processes list',
|
||||||
|
manageUsers: 'Manage users'
|
||||||
},
|
},
|
||||||
faker: {
|
faker: {
|
||||||
address: 'Address',
|
address: 'Address',
|
||||||
|
|
|
@ -38,6 +38,10 @@ export default class {
|
||||||
return ipcRenderer.invoke('get-version', uid);
|
return ipcRenderer.invoke('get-version', uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getProcesses (uid) {
|
||||||
|
return ipcRenderer.invoke('get-processes', uid);
|
||||||
|
}
|
||||||
|
|
||||||
static useSchema (params) {
|
static useSchema (params) {
|
||||||
return ipcRenderer.invoke('use-schema', params);
|
return ipcRenderer.invoke('use-schema', params);
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
"blob": $blob-color,
|
"blob": $blob-color,
|
||||||
"tinyblob": $blob-color,
|
"tinyblob": $blob-color,
|
||||||
"mediumblob": $blob-color,
|
"mediumblob": $blob-color,
|
||||||
|
"medium_blob": $blob-color,
|
||||||
"longblob": $blob-color,
|
"longblob": $blob-color,
|
||||||
"enum": $enum-color,
|
"enum": $enum-color,
|
||||||
"set": $enum-color,
|
"set": $enum-color,
|
||||||
|
|
|
@ -96,6 +96,10 @@ body {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.process-row .td:last-child {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
// Scrollbars
|
// Scrollbars
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 10px;
|
width: 10px;
|
||||||
|
|
Loading…
Reference in New Issue