scinote-web/app/javascript/vue/shared/datatable/table.vue

339 lines
8.8 KiB
Vue
Raw Normal View History

<template>
<div class="flex flex-col h-full">
2023-11-10 20:34:36 +08:00
<div class="relative flex flex-col flex-grow z-10">
2023-11-24 18:08:28 +08:00
<Toolbar
:toolbarActions="toolbarActions"
@toolbar:action="emitAction"
:searchValue="searchValue"
@search:change="setSearchValue"
:activePageUrl="activePageUrl"
:archivedPageUrl="archivedPageUrl"
:currentViewMode="currentViewMode"
2023-12-01 07:01:08 +08:00
:currentViewRender="currentViewRender"
:viewRenders="viewRenders"
2023-11-24 18:08:28 +08:00
:filters="filters"
@applyFilters="applyFilters"
2023-12-01 07:01:08 +08:00
@setTableView="switchViewRender('table')"
@setCardsView="switchViewRender('cards')"
2023-11-24 18:08:28 +08:00
/>
2023-12-01 07:01:08 +08:00
<div v-if="currentViewRender === 'cards'" class="flex-grow basis-64 overflow-y-auto overflow-x-visible p-2 -ml-2">
<div class="grid grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 gap-4">
<slot v-for="element in rowData" :key="element.id" name="card" :dtComponent="this" :params="element"></slot>
</div>
</div>
<ag-grid-vue
2023-12-01 07:01:08 +08:00
v-if="currentViewRender === 'table'"
2023-11-10 20:34:36 +08:00
class="ag-theme-alpine w-full flex-grow h-full z-10"
:class="{'opacity-0': initializing}"
2023-12-05 03:59:16 +08:00
:columnDefs="extendedColumnDefs"
:rowData="rowData"
:defaultColDef="defaultColDef"
:rowSelection="'multiple'"
2023-11-10 20:34:36 +08:00
:suppressRowTransform="true"
:gridOptions="gridOptions"
2023-11-10 20:34:36 +08:00
:suppressRowClickSelection="true"
@grid-ready="onGridReady"
@first-data-rendered="onFirstDataRendered"
@sortChanged="setOrder"
@columnResized="saveColumnsState"
@columnMoved="saveColumnsState"
@rowSelected="setSelectedRows"
2023-11-10 20:34:36 +08:00
@cellClicked="clickCell"
:CheckboxSelectionCallback="withCheckboxes"
>
</ag-grid-vue>
<ActionToolbar v-if="selectedRows.length > 0 && actionsUrl" :actionsUrl="actionsUrl" :params="actionsParams" @toolbar:action="emitAction" />
</div>
<div class="flex items-center py-4">
<div class="mr-auto">
<Pagination
:totalPage="totalPage"
:currentPage="page"
@setPage="setPage"
></Pagination>
</div>
<div class="flex items-center gap-4">
{{ i18n.t('datatable.show') }}
<div class="w-36">
<SelectDropdown
:value="perPage"
:options="perPageOptions"
@change="setPerPage"
></SelectDropdown>
</div>
</div>
</div>
</div>
</template>
<script>
import { AgGridVue } from "ag-grid-vue3";
import axios from '../../../packs/custom_axios.js';
import SelectDropdown from '../select_dropdown.vue';
import PerfectScrollbar from 'vue3-perfect-scrollbar';
import Pagination from './pagination.vue';
import CustomHeader from './tableHeader';
import ActionToolbar from './action_toolbar.vue';
import Toolbar from './toolbar.vue';
2023-11-10 20:34:36 +08:00
import RowMenuRenderer from './row_menu_renderer.vue';
export default {
name: "App",
props: {
withCheckboxes: {
type: Boolean,
default: true,
},
2023-11-10 20:34:36 +08:00
withRowMenu: {
type: Boolean,
default: false,
},
tableId: {
type: String,
required: true,
},
columnDefs: {
type: Array,
default: () => [],
},
dataUrl: {
type: String,
required: true,
},
actionsUrl: {
type: String,
},
toolbarActions: {
type: Object,
required: true
},
reloadingTable: {
type: Boolean,
default: false
2023-11-24 18:08:28 +08:00
},
activePageUrl: {
type: String,
},
archivedPageUrl: {
type: String,
},
currentViewMode: {
type: String,
default: 'active'
},
2023-12-01 07:01:08 +08:00
viewRenders: {
type: Object,
},
2023-11-24 18:08:28 +08:00
filters: {
type: Array,
default: () => []
2023-12-01 07:01:08 +08:00
}
},
data() {
return {
rowData: [],
gridApi: null,
columnApi: null,
defaultColDef: {
resizable: true
},
perPage: 20,
page: 1,
order: null,
totalPage: 0,
selectedRows: [],
searchValue: '',
2023-11-24 18:08:28 +08:00
initializing: true,
2023-12-01 07:01:08 +08:00
activeFilters: {},
currentViewRender: 'table',
cardCheckboxes: []
};
},
components: {
AgGridVue,
SelectDropdown,
PerfectScrollbar,
Pagination,
agColumnHeader: CustomHeader,
ActionToolbar,
2023-11-10 20:34:36 +08:00
Toolbar,
RowMenuRenderer
},
computed: {
perPageOptions() {
return [10, 20, 50, 100].map(value => [ value, `${value} ${this.i18n.t('datatable.rows')}` ]);
},
tableState() {
if (!localStorage.getItem(`datatable:${this.tableId}_columns_state`)) return null;
return JSON.parse(localStorage.getItem(`datatable:${this.tableId}_columns_state`));
},
actionsParams() {
return {
2023-11-10 20:34:36 +08:00
items: JSON.stringify(this.selectedRows.map(row => { return {id: row.id, type: row.type} }))
}
},
gridOptions() {
return {
suppressCellFocus: true
}
2023-12-05 03:59:16 +08:00
},
extendedColumnDefs() {
let columns = this.columnDefs.map(column => {
return {
...column,
cellRendererParams: {
dtComponent: this
}
}
2023-11-10 20:34:36 +08:00
});
2023-12-05 03:59:16 +08:00
if (this.withCheckboxes) {
columns.unshift({
field: "checkbox",
headerCheckboxSelection: true,
headerCheckboxSelectionFilteredOnly: true,
checkboxSelection: true,
width: 48,
minWidth: 48,
resizable: false,
pinned: 'left'
});
}
2023-11-10 20:34:36 +08:00
2023-12-05 03:59:16 +08:00
if (this.withRowMenu) {
columns.push({
field: "rowMenu",
headerName: '',
width: 42,
minWidth: 42,
resizable: false,
sortable: false,
cellRenderer: 'RowMenuRenderer',
cellRendererParams: {
dtComponent: this
},
pinned: 'right',
cellStyle: {padding: 0, display: 'flex', justifyContent: 'center', alignItems: 'center', overflow: 'visible'}
});
}
return columns;
}
},
watch: {
reloadingTable() {
if (this.reloadingTable) {
this.loadData();
}
}
},
mounted() {
this.loadData();
window.addEventListener('resize', this.resize);
},
beforeDestroy() {
window.removeEventListener('resize', this.resize);
},
methods: {
formatData(data) {
2023-11-10 20:34:36 +08:00
return data.map( (item) => Object.assign({}, item.attributes, { id: item.id, type: item.type }) );
},
resize() {
if (this.tableState) return;
this.gridApi.sizeColumnsToFit();
},
loadData() {
axios
.get(this.dataUrl, {
params: {
per_page: this.perPage,
page: this.page,
order: this.order,
2023-11-24 18:08:28 +08:00
search: this.searchValue,
view_mode: this.currentViewMode,
filters: this.activeFilters
}
})
.then((response) => {
this.selectedRows = [];
this.gridApi.setRowData(this.formatData(response.data.data));
2023-12-01 07:01:08 +08:00
this.rowData = this.formatData(response.data.data);
this.totalPage = response.data.meta.total_pages;
this.$emit('tableReloaded');
})
},
onGridReady(params) {
this.gridApi = params.api;
this.columnApi = params.columnApi;
if (this.tableState) {
this.columnApi.applyColumnState({
state: this.tableState,
applyOrder: true
});
}
setTimeout(() => {
this.initializing = false;
}, 200);
},
onFirstDataRendered(params) {
this.resize();
},
setPerPage(value) {
this.perPage = value;
2023-12-01 07:01:08 +08:00
this.page = 1;
this.loadData();
},
setPage(page) {
this.page = page;
this.loadData();
},
setOrder() {
const orderState = this.columnApi.getColumnState().filter(column => column.sort).map(column => {
return {
column: column.colId,
dir: column.sort
}
});
this.order = orderState[0];
this.saveColumnsState();
this.loadData();
},
saveColumnsState() {
if (!this.columnApi) return;
const columnsState = this.columnApi.getColumnState();
localStorage.setItem(`datatable:${this.tableId}_columns_state`, JSON.stringify(columnsState));
},
setSelectedRows() {
this.selectedRows = this.gridApi.getSelectedRows();
},
emitAction(action) {
this.$emit(action.name, action, this.selectedRows);
},
setSearchValue(value) {
this.searchValue = value;
this.loadData();
2023-11-10 20:34:36 +08:00
},
clickCell(e) {
2023-12-05 03:59:16 +08:00
if (e.column.colId !== 'rowMenu' && e.column.userProvidedColDef.notSelectable !== true) {
2023-11-10 20:34:36 +08:00
e.node.setSelected(true);
}
2023-11-24 18:08:28 +08:00
},
applyFilters(filters) {
this.activeFilters = filters;
this.loadData();
2023-12-01 07:01:08 +08:00
},
switchViewRender(view) {
if (this.currentViewRender === view) return;
this.currentViewRender = view;
this.initializing = true;
this.selectedRows = [];
}
}
};
</script>