antares/src/renderer/components/WorkspaceQueryTable.vue

337 lines
10 KiB
Vue
Raw Normal View History

2020-06-10 19:29:10 +02:00
<template>
<div
ref="tableWrapper"
class="vscroll"
:style="{'height': resultsSize+'px'}"
>
<TableContext
v-if="isContext"
:context-event="contextEvent"
2020-07-22 18:30:52 +02:00
:selected-rows="selectedRows"
@delete-selected="deleteSelected"
2020-08-12 18:11:48 +02:00
@close-context="isContext = false"
/>
<ul v-if="results.length > 1" class="tab tab-block result-tabs">
<li
v-for="(result, index) in results"
:key="index"
class="tab-item"
:class="{'active': resultsetIndex === index}"
@click="selectResultset(index)"
>
<a>{{ result.fields[0].orgTable }} ({{ result.rows.length }})</a>
</li>
</ul>
<div ref="table" class="table table-hover">
<div class="thead">
<div class="tr">
<div
v-for="field in fields"
:key="field.name"
class="th c-hand"
>
<div ref="columnResize" class="column-resizable">
<div class="table-column-title" @click="sort(field.name)">
<i
v-if="field.key"
class="mdi mdi-key column-key c-help"
:class="`key-${field.key}`"
:title="keyName(field.key)"
/>
<span>{{ field.alias || field.name }}</span>
<i
v-if="currentSort === field.name"
class="mdi sort-icon"
:class="currentSortDir === 'asc' ? 'mdi-sort-ascending':'mdi-sort-descending'"
/>
2020-06-16 18:01:22 +02:00
</div>
2020-06-11 23:34:38 +02:00
</div>
</div>
2020-06-10 19:29:10 +02:00
</div>
</div>
<BaseVirtualScroll
v-if="results[resultsetIndex] && results[resultsetIndex].rows"
ref="resultTable"
:items="sortedResults"
:item-height="22"
class="tbody"
:visible-height="resultsSize"
:scroll-element="scrollElement"
>
<template slot-scope="{ items }">
<WorkspaceQueryTableRow
v-for="row in items"
:key="row._id"
:row="row"
:fields="fields"
:key-usage="keyUsage"
class="tr"
:class="{'selected': selectedRows.includes(row._id)}"
@select-row="selectRow($event, row._id)"
2020-09-01 19:23:13 +02:00
@update-field="updateField($event, row[primaryField.alias || primaryField.name])"
@contextmenu="contextMenu"
/>
</template>
</basevirtualscroll>
</div>
</div>
2020-06-10 19:29:10 +02:00
</template>
<script>
import { uidGen } from 'common/libs/uidGen';
2020-06-11 23:34:38 +02:00
import BaseVirtualScroll from '@/components/BaseVirtualScroll';
import WorkspaceQueryTableRow from '@/components/WorkspaceQueryTableRow';
import TableContext from '@/components/WorkspaceQueryTableContext';
import { mapActions, mapGetters } from 'vuex';
2020-06-11 23:34:38 +02:00
2020-06-10 19:29:10 +02:00
export default {
name: 'WorkspaceQueryTable',
2020-06-11 23:34:38 +02:00
components: {
2020-06-26 18:14:16 +02:00
BaseVirtualScroll,
WorkspaceQueryTableRow,
TableContext
2020-06-18 19:01:09 +02:00
},
2020-06-10 19:29:10 +02:00
props: {
results: Array,
tabUid: [String, Number]
2020-06-10 19:29:10 +02:00
},
2020-06-11 23:34:38 +02:00
data () {
return {
2020-06-16 18:01:22 +02:00
resultsSize: 1000,
localResults: [],
isContext: false,
2020-07-22 18:30:52 +02:00
contextEvent: null,
selectedCell: null,
2020-07-24 13:26:56 +02:00
selectedRows: [],
currentSort: '',
currentSortDir: 'asc',
resultsetIndex: 0,
scrollElement: null
2020-06-11 23:34:38 +02:00
};
},
2020-06-26 18:14:16 +02:00
computed: {
...mapGetters({
getWorkspaceTab: 'workspaces/getWorkspaceTab'
}),
2020-06-26 18:14:16 +02:00
primaryField () {
return this.fields.filter(field => ['pri', 'uni'].includes(field.key))[0] || false;
2020-07-24 13:26:56 +02:00
},
sortedResults () {
if (this.currentSort) {
return [...this.localResults].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.localResults;
},
fields () {
return this.getWorkspaceTab(this.tabUid) && this.getWorkspaceTab(this.tabUid).fields[this.resultsetIndex] ? this.getWorkspaceTab(this.tabUid).fields[this.resultsetIndex] : [];
},
keyUsage () {
return this.getWorkspaceTab(this.tabUid) && this.getWorkspaceTab(this.tabUid).keyUsage[this.resultsetIndex] ? this.getWorkspaceTab(this.tabUid).keyUsage[this.resultsetIndex] : [];
2020-06-26 18:14:16 +02:00
}
},
2020-06-16 18:01:22 +02:00
watch: {
results () {
this.setLocalResults();
this.resultsetIndex = 0;
},
resultsetIndex () {
this.setLocalResults();
2020-06-11 23:34:38 +02:00
}
},
2020-06-12 18:10:45 +02:00
updated () {
if (this.$refs.table)
2020-07-24 13:26:56 +02:00
this.refreshScroller();
if (this.$refs.tableWrapper)
this.scrollElement = this.$refs.tableWrapper;
2020-06-11 23:34:38 +02:00
},
mounted () {
window.addEventListener('resize', this.resizeResults);
},
destroyed () {
window.removeEventListener('resize', this.resizeResults);
},
2020-06-10 19:29:10 +02:00
methods: {
2020-06-26 18:14:16 +02:00
...mapActions({
addNotification: 'notifications/addNotification'
}),
2020-06-18 19:01:09 +02:00
fieldType (cKey) {
let type = 'unknown';
const field = this.fields.filter(field => field.name === cKey)[0];
if (field)
type = field.type;
2020-06-10 19:29:10 +02:00
2020-06-18 19:01:09 +02:00
return type;
},
2020-07-05 16:06:56 +02:00
fieldPrecision (cKey) {
let length = 0;
const field = this.fields.filter(field => field.name === cKey)[0];
if (field)
length = field.datePrecision;
2020-07-05 16:06:56 +02:00
return length;
},
2020-06-16 18:01:22 +02:00
keyName (key) {
switch (key) {
case 'pri':
return 'PRIMARY';
case 'uni':
return 'UNIQUE';
case 'mul':
return 'INDEX';
default:
return 'UNKNOWN ' + key;
}
},
setLocalResults () {
this.resetSort();
this.localResults = this.results[this.resultsetIndex] && this.results[this.resultsetIndex].rows ? this.results[this.resultsetIndex].rows.map(item => {
return { ...item, _id: uidGen() };
}) : [];
},
2020-07-23 19:10:14 +02:00
resizeResults () {
2020-06-12 18:10:45 +02:00
if (this.$refs.resultTable) {
const el = this.$refs.tableWrapper;
2020-06-11 23:34:38 +02:00
2020-06-12 18:10:45 +02:00
if (el) {
const footer = document.getElementById('footer');
2020-06-12 18:10:45 +02:00
const size = window.innerHeight - el.getBoundingClientRect().top - footer.offsetHeight;
this.resultsSize = size;
}
2020-07-23 19:10:14 +02:00
this.$refs.resultTable.updateWindow();
2020-06-11 23:34:38 +02:00
}
2020-06-26 18:14:16 +02:00
},
2020-07-23 19:10:14 +02:00
refreshScroller () {
this.resizeResults();
},
2020-07-22 18:30:52 +02:00
updateField (payload, id) {
2020-06-26 18:14:16 +02:00
if (!this.primaryField)
this.addNotification({ status: 'warning', message: this.$t('message.unableEditFieldWithoutPrimary') });
2020-06-27 15:14:08 +02:00
else {
const params = {
primary: this.primaryField.name,
id,
2020-07-22 18:30:52 +02:00
...payload
2020-06-27 15:14:08 +02:00
};
this.$emit('update-field', params);
2020-06-27 15:14:08 +02:00
}
2020-06-28 15:31:16 +02:00
},
2020-07-23 19:10:14 +02:00
deleteSelected () {
if (!this.primaryField)
this.addNotification({ status: 'warning', message: this.$t('message.unableEditFieldWithoutPrimary') });
else {
const rowIDs = this.localResults.filter(row => this.selectedRows.includes(row._id)).map(row => row[this.primaryField.name]);
const params = {
primary: this.primaryField.name,
rows: rowIDs
};
this.$emit('delete-selected', params);
2020-07-23 19:10:14 +02:00
}
},
2020-06-28 15:31:16 +02:00
applyUpdate (params) {
const { primary, id, field, content } = params;
this.localResults = this.localResults.map(row => {
if (row[primary] === id)
row[field] = content;
return row;
});
},
2020-07-22 18:30:52 +02:00
selectRow (event, row) {
if (event.ctrlKey) {
if (this.selectedRows.includes(row))
this.selectedRows = this.selectedRows.filter(el => el !== row);
else
this.selectedRows.push(row);
}
else if (event.shiftKey) {
if (!this.selectedRows.length)
this.selectedRows.push(row);
else {
const lastID = this.selectedRows.slice(-1)[0];
const lastIndex = this.localResults.findIndex(el => el._id === lastID);
const clickedIndex = this.localResults.findIndex(el => el._id === row);
if (lastIndex > clickedIndex) {
for (let i = clickedIndex; i < lastIndex; i++)
this.selectedRows.push(this.localResults[i]._id);
}
else if (lastIndex < clickedIndex) {
for (let i = clickedIndex; i > lastIndex; i--)
this.selectedRows.push(this.localResults[i]._id);
}
}
}
else
this.selectedRows = [row];
},
contextMenu (event, cell) {
this.selectedCell = cell;
if (!this.selectedRows.includes(cell.id))
this.selectedRows = [cell.id];
this.contextEvent = event;
this.isContext = true;
2020-07-24 13:26:56 +02:00
},
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';
},
selectResultset (index) {
this.resultsetIndex = index;
2020-06-10 19:29:10 +02:00
}
}
};
</script>
<style lang="scss" scoped>
2020-06-11 23:34:38 +02:00
.vscroll {
2020-07-31 18:16:28 +02:00
height: 1000px;
overflow: auto;
overflow-anchor: none;
2020-06-11 23:34:38 +02:00
}
2020-06-16 18:01:22 +02:00
2020-07-31 18:16:28 +02:00
.column-resizable {
&:hover,
&:active {
resize: horizontal;
overflow: hidden;
}
2020-07-24 13:26:56 +02:00
}
2020-07-31 18:16:28 +02:00
.table-column-title {
display: flex;
align-items: center;
2020-06-16 18:01:22 +02:00
}
2020-07-31 18:16:28 +02:00
.sort-icon {
font-size: 0.7rem;
line-height: 1;
margin-left: 0.2rem;
2020-07-24 13:26:56 +02:00
}
.result-tabs {
background: transparent !important;
margin: 0;
}
2020-06-10 19:29:10 +02:00
</style>