mirror of
https://github.com/Fabio286/antares.git
synced 2025-02-02 10:27:19 +01:00
Merge pull request #123 from toriphes/master
feat(UI): multi column table filters
This commit is contained in:
commit
ea65d8eee7
@ -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 {
|
||||
const offset = (page - 1) * limit;
|
||||
const query = connections[uid]
|
||||
@ -29,6 +29,9 @@ export default (connections) => {
|
||||
if (sortParams && sortParams.field && sortParams.dir)
|
||||
query.orderBy({ [sortParams.field]: sortParams.dir.toUpperCase() });
|
||||
|
||||
if (where)
|
||||
query.where(where);
|
||||
|
||||
const result = await query.run({ details: true, schema });
|
||||
|
||||
return { status: 'success', response: result };
|
||||
|
@ -71,6 +71,13 @@
|
||||
|
||||
<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
|
||||
v-if="isTable"
|
||||
class="btn btn-dark btn-sm"
|
||||
@ -121,6 +128,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<WorkspaceTabTableFilters
|
||||
v-if="isSearch"
|
||||
:fields="fields"
|
||||
@filter="updateFilters"
|
||||
@filter-change="onFilterChange"
|
||||
/>
|
||||
<div class="workspace-query-results p-relative column col-12">
|
||||
<BaseLoader v-if="isQuering" />
|
||||
<WorkspaceTabQueryTable
|
||||
@ -160,6 +173,7 @@
|
||||
import Tables from '@/ipc-api/Tables';
|
||||
import BaseLoader from '@/components/BaseLoader';
|
||||
import WorkspaceTabQueryTable from '@/components/WorkspaceTabQueryTable';
|
||||
import WorkspaceTabTableFilters from '@/components/WorkspaceTabTableFilters';
|
||||
import ModalNewTableRow from '@/components/ModalNewTableRow';
|
||||
import ModalFakerRows from '@/components/ModalFakerRows';
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
@ -170,6 +184,7 @@ export default {
|
||||
components: {
|
||||
BaseLoader,
|
||||
WorkspaceTabQueryTable,
|
||||
WorkspaceTabTableFilters,
|
||||
ModalNewTableRow,
|
||||
ModalFakerRows
|
||||
},
|
||||
@ -192,6 +207,7 @@ export default {
|
||||
tabUid: 'data', // ???
|
||||
isQuering: false,
|
||||
isPageMenu: false,
|
||||
isSearch: false,
|
||||
results: [],
|
||||
lastTable: null,
|
||||
isAddModal: false,
|
||||
@ -199,6 +215,7 @@ export default {
|
||||
autorefreshTimer: 0,
|
||||
refreshInterval: null,
|
||||
sortParams: {},
|
||||
filters: [],
|
||||
page: 1,
|
||||
pageProxy: 1,
|
||||
approximateCount: 0
|
||||
@ -271,6 +288,13 @@ export default {
|
||||
if (this.lastTable !== this.table)
|
||||
this.getTableData();
|
||||
}
|
||||
},
|
||||
isSearch (val) {
|
||||
if (this.filters.length > 0 && !val) {
|
||||
this.filters = [];
|
||||
this.getTableData();
|
||||
}
|
||||
this.resizeScroller();
|
||||
}
|
||||
},
|
||||
created () {
|
||||
@ -302,7 +326,8 @@ export default {
|
||||
table: this.table,
|
||||
limit: this.limit,
|
||||
page: this.page,
|
||||
sortParams: this.sortParams
|
||||
sortParams: this.sortParams,
|
||||
where: this.filters || []
|
||||
};
|
||||
|
||||
try { // Table data
|
||||
@ -389,11 +414,13 @@ export default {
|
||||
if (e.key === 'F5')
|
||||
this.reloadTable();
|
||||
|
||||
if (e.ctrlKey) {
|
||||
if (e.ctrlKey || e.metaKey) {
|
||||
if (e.key === 'ArrowRight')
|
||||
this.pageChange('next');
|
||||
if (e.key === 'ArrowLeft')
|
||||
this.pageChange('prev');
|
||||
if (e.keyCode === 70) // f
|
||||
this.isSearch = !this.isSearch;
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -410,6 +437,18 @@ export default {
|
||||
},
|
||||
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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
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',
|
||||
history: 'History',
|
||||
select: 'Select',
|
||||
passphrase: 'Passphrase'
|
||||
passphrase: 'Passphrase',
|
||||
filter: 'Filter'
|
||||
},
|
||||
message: {
|
||||
appWelcome: 'Welcome to Antares SQL Client!',
|
||||
|
@ -115,7 +115,13 @@ module.exports = {
|
||||
cell: 'Cella | Celle',
|
||||
triggerFunction: 'Funzione di trigger | Funzioni di trigger',
|
||||
all: 'Tutto',
|
||||
duplicate: 'Duplica'
|
||||
duplicate: 'Duplica',
|
||||
routine: 'Routine',
|
||||
new: 'Nuovo',
|
||||
history: 'Cronologia',
|
||||
select: 'Seleziona',
|
||||
passphrase: 'Passphrase',
|
||||
filter: 'Filtra'
|
||||
},
|
||||
message: {
|
||||
appWelcome: 'Benvenuto in Antares SQL Client!',
|
||||
|
Loading…
x
Reference in New Issue
Block a user