mirror of
https://github.com/Fabio286/antares.git
synced 2025-06-05 21:59:22 +02:00
Merge pull request #123 from toriphes/master
feat(UI): multi column table filters
This commit is contained in:
@ -16,7 +16,7 @@ export default (connections) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('get-table-data', async (event, { uid, schema, table, limit, page, sortParams }) => {
|
ipcMain.handle('get-table-data', async (event, { uid, schema, table, limit, page, sortParams, where }) => {
|
||||||
try {
|
try {
|
||||||
const offset = (page - 1) * limit;
|
const offset = (page - 1) * limit;
|
||||||
const query = connections[uid]
|
const query = connections[uid]
|
||||||
@ -29,6 +29,9 @@ export default (connections) => {
|
|||||||
if (sortParams && sortParams.field && sortParams.dir)
|
if (sortParams && sortParams.field && sortParams.dir)
|
||||||
query.orderBy({ [sortParams.field]: sortParams.dir.toUpperCase() });
|
query.orderBy({ [sortParams.field]: sortParams.dir.toUpperCase() });
|
||||||
|
|
||||||
|
if (where)
|
||||||
|
query.where(where);
|
||||||
|
|
||||||
const result = await query.run({ details: true, schema });
|
const result = await query.run({ details: true, schema });
|
||||||
|
|
||||||
return { status: 'success', response: result };
|
return { status: 'success', response: result };
|
||||||
|
@ -71,6 +71,13 @@
|
|||||||
|
|
||||||
<div class="divider-vert py-3" />
|
<div class="divider-vert py-3" />
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="btn btn-sm"
|
||||||
|
:class="{'btn-primary': isSearch, 'btn-dark': !isSearch}"
|
||||||
|
@click="isSearch = !isSearch"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-magnify" />
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="isTable"
|
v-if="isTable"
|
||||||
class="btn btn-dark btn-sm"
|
class="btn btn-dark btn-sm"
|
||||||
@ -121,6 +128,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<WorkspaceTabTableFilters
|
||||||
|
v-if="isSearch"
|
||||||
|
:fields="fields"
|
||||||
|
@filter="updateFilters"
|
||||||
|
@filter-change="onFilterChange"
|
||||||
|
/>
|
||||||
<div class="workspace-query-results p-relative column col-12">
|
<div class="workspace-query-results p-relative column col-12">
|
||||||
<BaseLoader v-if="isQuering" />
|
<BaseLoader v-if="isQuering" />
|
||||||
<WorkspaceTabQueryTable
|
<WorkspaceTabQueryTable
|
||||||
@ -160,6 +173,7 @@
|
|||||||
import Tables from '@/ipc-api/Tables';
|
import Tables from '@/ipc-api/Tables';
|
||||||
import BaseLoader from '@/components/BaseLoader';
|
import BaseLoader from '@/components/BaseLoader';
|
||||||
import WorkspaceTabQueryTable from '@/components/WorkspaceTabQueryTable';
|
import WorkspaceTabQueryTable from '@/components/WorkspaceTabQueryTable';
|
||||||
|
import WorkspaceTabTableFilters from '@/components/WorkspaceTabTableFilters';
|
||||||
import ModalNewTableRow from '@/components/ModalNewTableRow';
|
import ModalNewTableRow from '@/components/ModalNewTableRow';
|
||||||
import ModalFakerRows from '@/components/ModalFakerRows';
|
import ModalFakerRows from '@/components/ModalFakerRows';
|
||||||
import { mapGetters, mapActions } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
@ -170,6 +184,7 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
BaseLoader,
|
BaseLoader,
|
||||||
WorkspaceTabQueryTable,
|
WorkspaceTabQueryTable,
|
||||||
|
WorkspaceTabTableFilters,
|
||||||
ModalNewTableRow,
|
ModalNewTableRow,
|
||||||
ModalFakerRows
|
ModalFakerRows
|
||||||
},
|
},
|
||||||
@ -192,6 +207,7 @@ export default {
|
|||||||
tabUid: 'data', // ???
|
tabUid: 'data', // ???
|
||||||
isQuering: false,
|
isQuering: false,
|
||||||
isPageMenu: false,
|
isPageMenu: false,
|
||||||
|
isSearch: false,
|
||||||
results: [],
|
results: [],
|
||||||
lastTable: null,
|
lastTable: null,
|
||||||
isAddModal: false,
|
isAddModal: false,
|
||||||
@ -199,6 +215,7 @@ export default {
|
|||||||
autorefreshTimer: 0,
|
autorefreshTimer: 0,
|
||||||
refreshInterval: null,
|
refreshInterval: null,
|
||||||
sortParams: {},
|
sortParams: {},
|
||||||
|
filters: [],
|
||||||
page: 1,
|
page: 1,
|
||||||
pageProxy: 1,
|
pageProxy: 1,
|
||||||
approximateCount: 0
|
approximateCount: 0
|
||||||
@ -271,6 +288,13 @@ export default {
|
|||||||
if (this.lastTable !== this.table)
|
if (this.lastTable !== this.table)
|
||||||
this.getTableData();
|
this.getTableData();
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
isSearch (val) {
|
||||||
|
if (this.filters.length > 0 && !val) {
|
||||||
|
this.filters = [];
|
||||||
|
this.getTableData();
|
||||||
|
}
|
||||||
|
this.resizeScroller();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
@ -302,7 +326,8 @@ export default {
|
|||||||
table: this.table,
|
table: this.table,
|
||||||
limit: this.limit,
|
limit: this.limit,
|
||||||
page: this.page,
|
page: this.page,
|
||||||
sortParams: this.sortParams
|
sortParams: this.sortParams,
|
||||||
|
where: this.filters || []
|
||||||
};
|
};
|
||||||
|
|
||||||
try { // Table data
|
try { // Table data
|
||||||
@ -389,11 +414,13 @@ export default {
|
|||||||
if (e.key === 'F5')
|
if (e.key === 'F5')
|
||||||
this.reloadTable();
|
this.reloadTable();
|
||||||
|
|
||||||
if (e.ctrlKey) {
|
if (e.ctrlKey || e.metaKey) {
|
||||||
if (e.key === 'ArrowRight')
|
if (e.key === 'ArrowRight')
|
||||||
this.pageChange('next');
|
this.pageChange('next');
|
||||||
if (e.key === 'ArrowLeft')
|
if (e.key === 'ArrowLeft')
|
||||||
this.pageChange('prev');
|
this.pageChange('prev');
|
||||||
|
if (e.keyCode === 70) // f
|
||||||
|
this.isSearch = !this.isSearch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -410,6 +437,18 @@ export default {
|
|||||||
},
|
},
|
||||||
downloadTable (format) {
|
downloadTable (format) {
|
||||||
this.$refs.queryTable.downloadTable(format, this.table);
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
166
src/renderer/components/WorkspaceTabTableFilters.vue
Normal file
166
src/renderer/components/WorkspaceTabTableFilters.vue
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
<template>
|
||||||
|
<form class="workspace-table-filters" @submit.prevent="doFilter">
|
||||||
|
<div
|
||||||
|
v-for="(row, index) of rows"
|
||||||
|
:key="index"
|
||||||
|
class="workspace-table-filters-row"
|
||||||
|
>
|
||||||
|
<label class="form-checkbox my-0">
|
||||||
|
<input
|
||||||
|
v-model="row.active"
|
||||||
|
type="checkbox"
|
||||||
|
@change="doFilter"
|
||||||
|
><i class="form-icon" />
|
||||||
|
</label>
|
||||||
|
<select v-model="row.field" class="form-select col-auto select-sm">
|
||||||
|
<option
|
||||||
|
v-for="(item, j) of fields"
|
||||||
|
:key="j"
|
||||||
|
:value="item.name"
|
||||||
|
>
|
||||||
|
{{ item.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<select v-model="row.op" class="form-select ml-2 col-auto select-sm">
|
||||||
|
<option
|
||||||
|
v-for="(operator, k) of operators"
|
||||||
|
:key="k"
|
||||||
|
:value="operator"
|
||||||
|
>
|
||||||
|
{{ operator }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<div class="workspace-table-filters-row-value ml-2">
|
||||||
|
<input
|
||||||
|
v-if="!row.op.includes('NULL')"
|
||||||
|
v-model="row.value"
|
||||||
|
type="text"
|
||||||
|
class="form-input input-sm"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
v-if="row.op === 'BETWEEN'"
|
||||||
|
v-model="row.value2"
|
||||||
|
type="text"
|
||||||
|
class="form-input ml-2 input-sm"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="btn btn-sm btn-dark mr-0 ml-2"
|
||||||
|
type="button"
|
||||||
|
@click="removeRow(index)"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-minus-circle-outline" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="workspace-table-filters-buttons">
|
||||||
|
<button
|
||||||
|
class="btn btn-sm btn-primary mr-0 ml-2"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
{{ $t('word.filter') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-sm btn-dark mr-0 ml-2"
|
||||||
|
type="button"
|
||||||
|
@click="addRow"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-plus-circle-outline" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
fields: Array
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
rows: [],
|
||||||
|
operators: [
|
||||||
|
'=', '!=', '>', '<', '>=', '<=', 'IN', 'NOT IN', 'LIKE', 'BETWEEN', 'IS NULL', 'IS NOT NULL'
|
||||||
|
]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.addRow();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
addRow () {
|
||||||
|
this.rows.push({ active: true, field: this.fields[0].name, op: '=', value: '', value2: '' });
|
||||||
|
this.$emit('filter-change', this.rows);
|
||||||
|
},
|
||||||
|
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 = field.type.match(/INT|FLOAT|DECIMAL/);
|
||||||
|
let value = null;
|
||||||
|
|
||||||
|
switch (filter.op) {
|
||||||
|
case '=':
|
||||||
|
case '!=':
|
||||||
|
value = isNumeric ? filter.value : '"' + filter.value + '"';
|
||||||
|
break;
|
||||||
|
case 'BETWEEN':
|
||||||
|
value = isNumeric ? filter.value : '"' + filter.value + '"';
|
||||||
|
value += ' AND ';
|
||||||
|
value += isNumeric ? filter.value2 : '"' + filter.value2 + '"';
|
||||||
|
console.log(value);
|
||||||
|
break;
|
||||||
|
case 'IN':
|
||||||
|
case 'NOT IN':
|
||||||
|
value = filter.value.split(',').map(val => {
|
||||||
|
val = val.trim();
|
||||||
|
return isNumeric ? val : '"' + val + '"';
|
||||||
|
}).join(',');
|
||||||
|
value = '(' + filter.value + ')';
|
||||||
|
break;
|
||||||
|
case 'IS NULL':
|
||||||
|
case 'IS NOT NULL':
|
||||||
|
value = '';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
value = '"' + filter.value + '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNumeric && value.length === 0)
|
||||||
|
value = '""';
|
||||||
|
|
||||||
|
return `${filter.field} ${filter.op} ${value}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.workspace-table-filters {
|
||||||
|
padding: 0 0.6rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-table-filters-buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
padding-bottom: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-table-filters-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding-bottom: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-table-filters-row-value {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
</style>
|
@ -120,7 +120,8 @@ module.exports = {
|
|||||||
new: 'New',
|
new: 'New',
|
||||||
history: 'History',
|
history: 'History',
|
||||||
select: 'Select',
|
select: 'Select',
|
||||||
passphrase: 'Passphrase'
|
passphrase: 'Passphrase',
|
||||||
|
filter: 'Filter'
|
||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
appWelcome: 'Welcome to Antares SQL Client!',
|
appWelcome: 'Welcome to Antares SQL Client!',
|
||||||
|
@ -115,7 +115,13 @@ module.exports = {
|
|||||||
cell: 'Cella | Celle',
|
cell: 'Cella | Celle',
|
||||||
triggerFunction: 'Funzione di trigger | Funzioni di trigger',
|
triggerFunction: 'Funzione di trigger | Funzioni di trigger',
|
||||||
all: 'Tutto',
|
all: 'Tutto',
|
||||||
duplicate: 'Duplica'
|
duplicate: 'Duplica',
|
||||||
|
routine: 'Routine',
|
||||||
|
new: 'Nuovo',
|
||||||
|
history: 'Cronologia',
|
||||||
|
select: 'Seleziona',
|
||||||
|
passphrase: 'Passphrase',
|
||||||
|
filter: 'Filtra'
|
||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
appWelcome: 'Benvenuto in Antares SQL Client!',
|
appWelcome: 'Benvenuto in Antares SQL Client!',
|
||||||
|
Reference in New Issue
Block a user