2023-09-24 20:10:53 +08:00
|
|
|
<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>
|
2023-09-24 20:10:53 +08:00
|
|
|
<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"
|
2023-09-24 20:10:53 +08:00
|
|
|
:class="{'opacity-0': initializing}"
|
2023-12-05 03:59:16 +08:00
|
|
|
:columnDefs="extendedColumnDefs"
|
2023-09-24 20:10:53 +08:00
|
|
|
:rowData="rowData"
|
|
|
|
:defaultColDef="defaultColDef"
|
|
|
|
:rowSelection="'multiple'"
|
2023-11-10 20:34:36 +08:00
|
|
|
:suppressRowTransform="true"
|
2023-09-24 20:10:53 +08:00
|
|
|
:gridOptions="gridOptions"
|
2023-11-10 20:34:36 +08:00
|
|
|
:suppressRowClickSelection="true"
|
2023-09-24 20:10:53 +08:00
|
|
|
@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"
|
2023-09-24 20:10:53 +08:00
|
|
|
: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">
|
2023-11-29 06:47:20 +08:00
|
|
|
<SelectDropdown
|
2023-09-24 20:10:53 +08:00
|
|
|
:value="perPage"
|
|
|
|
:options="perPageOptions"
|
|
|
|
@change="setPerPage"
|
2023-11-29 06:47:20 +08:00
|
|
|
></SelectDropdown>
|
2023-09-24 20:10:53 +08:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
2023-11-09 18:36:44 +08:00
|
|
|
import { AgGridVue } from "ag-grid-vue3";
|
2023-09-24 20:10:53 +08:00
|
|
|
import axios from '../../../packs/custom_axios.js';
|
2023-11-29 06:47:20 +08:00
|
|
|
import SelectDropdown from '../select_dropdown.vue';
|
2023-11-09 18:36:44 +08:00
|
|
|
import PerfectScrollbar from 'vue3-perfect-scrollbar';
|
2023-09-24 20:10:53 +08:00
|
|
|
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';
|
2023-09-24 20:10:53 +08:00
|
|
|
|
|
|
|
export default {
|
|
|
|
name: "App",
|
|
|
|
props: {
|
|
|
|
withCheckboxes: {
|
|
|
|
type: Boolean,
|
|
|
|
default: true,
|
|
|
|
},
|
2023-11-10 20:34:36 +08:00
|
|
|
withRowMenu: {
|
|
|
|
type: Boolean,
|
|
|
|
default: false,
|
|
|
|
},
|
2023-09-24 20:10:53 +08:00
|
|
|
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
|
|
|
}
|
2023-09-24 20:10:53 +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: []
|
2023-09-24 20:10:53 +08:00
|
|
|
};
|
|
|
|
},
|
|
|
|
components: {
|
|
|
|
AgGridVue,
|
2023-11-29 06:47:20 +08:00
|
|
|
SelectDropdown,
|
2023-09-24 20:10:53 +08:00
|
|
|
PerfectScrollbar,
|
|
|
|
Pagination,
|
|
|
|
agColumnHeader: CustomHeader,
|
|
|
|
ActionToolbar,
|
2023-11-10 20:34:36 +08:00
|
|
|
Toolbar,
|
|
|
|
RowMenuRenderer
|
2023-09-24 20:10:53 +08:00
|
|
|
},
|
|
|
|
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} }))
|
2023-09-24 20:10:53 +08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
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;
|
2023-09-24 20:10:53 +08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
watch: {
|
|
|
|
reloadingTable() {
|
|
|
|
if (this.reloadingTable) {
|
|
|
|
this.loadData();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
mounted() {
|
|
|
|
this.loadData();
|
|
|
|
window.addEventListener('resize', this.resize);
|
|
|
|
},
|
|
|
|
beforeDestroy() {
|
|
|
|
window.removeEventListener('resize', this.resize);
|
|
|
|
},
|
|
|
|
methods: {
|
2023-11-09 23:58:20 +08:00
|
|
|
formatData(data) {
|
2023-11-10 20:34:36 +08:00
|
|
|
return data.map( (item) => Object.assign({}, item.attributes, { id: item.id, type: item.type }) );
|
2023-11-09 23:58:20 +08:00
|
|
|
},
|
2023-09-24 20:10:53 +08:00
|
|
|
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
|
2023-09-24 20:10:53 +08:00
|
|
|
}
|
|
|
|
})
|
|
|
|
.then((response) => {
|
|
|
|
this.selectedRows = [];
|
2023-11-09 23:58:20 +08:00
|
|
|
this.gridApi.setRowData(this.formatData(response.data.data));
|
2023-12-01 07:01:08 +08:00
|
|
|
this.rowData = this.formatData(response.data.data);
|
2023-11-09 23:58:20 +08:00
|
|
|
this.totalPage = response.data.meta.total_pages;
|
2023-09-24 20:10:53 +08:00
|
|
|
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;
|
2023-09-24 20:10:53 +08:00
|
|
|
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 = [];
|
2023-09-24 20:10:53 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
</script>
|