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 }) => {
|
||||
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
|
||||
*
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
<slot name="body" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer pt-0">
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
class="btn btn-primary mr-2"
|
||||
@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"
|
||||
class="tab tab-block column col-12"
|
||||
>
|
||||
<li class="tab-item d-none">
|
||||
<a class="tab-link workspace-tools-link">
|
||||
<li class="tab-item dropdown tools-dropdown">
|
||||
<a
|
||||
class="tab-link workspace-tools-link dropdown-toggle"
|
||||
tabindex="0"
|
||||
:title="$t('word.tools')"
|
||||
>
|
||||
<i class="mdi mdi-24px mdi-tools" />
|
||||
</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
|
||||
v-if="schemaChild"
|
||||
|
@ -19,7 +43,7 @@
|
|||
@click="selectTab({uid: workspace.uid, tab: 'prop'})"
|
||||
>
|
||||
<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>
|
||||
</a>
|
||||
</li>
|
||||
|
@ -114,6 +138,11 @@
|
|||
:connection="connection"
|
||||
/>
|
||||
</div>
|
||||
<ModalProcessesList
|
||||
v-if="isProcessesModal"
|
||||
:connection="connection"
|
||||
@close="hideProcessesModal"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -129,6 +158,7 @@ import WorkspacePropsTabTrigger from '@/components/WorkspacePropsTabTrigger';
|
|||
import WorkspacePropsTabRoutine from '@/components/WorkspacePropsTabRoutine';
|
||||
import WorkspacePropsTabFunction from '@/components/WorkspacePropsTabFunction';
|
||||
import WorkspacePropsTabScheduler from '@/components/WorkspacePropsTabScheduler';
|
||||
import ModalProcessesList from '@/components/ModalProcessesList';
|
||||
|
||||
export default {
|
||||
name: 'Workspace',
|
||||
|
@ -141,14 +171,16 @@ export default {
|
|||
WorkspacePropsTabTrigger,
|
||||
WorkspacePropsTabRoutine,
|
||||
WorkspacePropsTabFunction,
|
||||
WorkspacePropsTabScheduler
|
||||
WorkspacePropsTabScheduler,
|
||||
ModalProcessesList
|
||||
},
|
||||
props: {
|
||||
connection: Object
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
hasWheelEvent: false
|
||||
hasWheelEvent: false,
|
||||
isProcessesModal: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -234,6 +266,12 @@ export default {
|
|||
closeTab (tUid) {
|
||||
if (this.queryTabs.length === 1) return;
|
||||
this.removeTab({ uid: this.connection.uid, tab: tUid });
|
||||
},
|
||||
showProcessesModal () {
|
||||
this.isProcessesModal = true;
|
||||
},
|
||||
hideProcessesModal () {
|
||||
this.isProcessesModal = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -298,6 +336,48 @@ export default {
|
|||
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 {
|
||||
padding-bottom: 0;
|
||||
padding-top: 0.3rem;
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
/>
|
||||
<span>{{ field.alias || field.name }}</span>
|
||||
<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="currentSortDir === 'asc' ? 'mdi-sort-ascending':'mdi-sort-descending'"
|
||||
/>
|
||||
|
@ -67,7 +67,6 @@
|
|||
:row="row"
|
||||
:fields="fieldsObj"
|
||||
:key-usage="keyUsage"
|
||||
class="tr"
|
||||
:class="{'selected': selectedRows.includes(row._id)}"
|
||||
@select-row="selectRow($event, row._id)"
|
||||
@update-field="updateField($event, row)"
|
||||
|
@ -97,7 +96,6 @@ export default {
|
|||
},
|
||||
props: {
|
||||
results: Array,
|
||||
tabUid: [String, Number],
|
||||
connUid: String,
|
||||
mode: String
|
||||
},
|
||||
|
@ -125,7 +123,7 @@ export default {
|
|||
primaryField () {
|
||||
return this.fields.filter(field => ['pri', 'uni'].includes(field.key))[0] || false;
|
||||
},
|
||||
idSortable () {
|
||||
isSortable () {
|
||||
return this.fields.every(field => field.name);
|
||||
},
|
||||
isHardSort () {
|
||||
|
@ -380,7 +378,7 @@ export default {
|
|||
this.isContext = true;
|
||||
},
|
||||
sort (field) {
|
||||
if (!this.idSortable) return;
|
||||
if (!this.isSortable) return;
|
||||
|
||||
if (this.mode === 'query')
|
||||
field = `${this.getTable(this.resultsetIndex)}.${field}`;
|
||||
|
|
|
@ -97,7 +97,9 @@ module.exports = {
|
|||
content: 'Content',
|
||||
cut: 'Cut',
|
||||
copy: 'Copy',
|
||||
paste: 'Paste'
|
||||
paste: 'Paste',
|
||||
tools: 'Tools',
|
||||
variables: 'Variables'
|
||||
},
|
||||
message: {
|
||||
appWelcome: 'Welcome to Antares SQL Client!',
|
||||
|
@ -193,7 +195,9 @@ module.exports = {
|
|||
selectAll: 'Select all',
|
||||
queryDuration: 'Query duration',
|
||||
includeBetaUpdates: 'Include beta updates',
|
||||
setNull: 'Set NULL'
|
||||
setNull: 'Set NULL',
|
||||
processesList: 'Processes list',
|
||||
manageUsers: 'Manage users'
|
||||
},
|
||||
faker: {
|
||||
address: 'Address',
|
||||
|
|
|
@ -38,6 +38,10 @@ export default class {
|
|||
return ipcRenderer.invoke('get-version', uid);
|
||||
}
|
||||
|
||||
static getProcesses (uid) {
|
||||
return ipcRenderer.invoke('get-processes', uid);
|
||||
}
|
||||
|
||||
static useSchema (params) {
|
||||
return ipcRenderer.invoke('use-schema', params);
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
"blob": $blob-color,
|
||||
"tinyblob": $blob-color,
|
||||
"mediumblob": $blob-color,
|
||||
"medium_blob": $blob-color,
|
||||
"longblob": $blob-color,
|
||||
"enum": $enum-color,
|
||||
"set": $enum-color,
|
||||
|
|
|
@ -96,6 +96,10 @@ body {
|
|||
}
|
||||
}
|
||||
|
||||
.process-row .td:last-child {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
// Scrollbars
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
|
|
Loading…
Reference in New Issue