show/hide columns and load default view
gitea/salix/pipeline/head This commit looks good Details

This commit is contained in:
Carlos Jimenez Ruiz 2021-10-27 17:11:33 +02:00
parent c976f1ed07
commit b27a443729
9 changed files with 188 additions and 19 deletions

View File

@ -7,8 +7,7 @@ module.exports = function(Self) {
required: true, required: true,
description: `Code of the table you ask its configuration`, description: `Code of the table you ask its configuration`,
http: {source: 'body'} http: {source: 'body'}
} }],
],
returns: { returns: {
type: 'object', type: 'object',
root: true root: true
@ -29,6 +28,6 @@ module.exports = function(Self) {
config.userFk = ctx.req.accessToken.userId; config.userFk = ctx.req.accessToken.userId;
return await Self.app.models.UserConfigView.create(config); return Self.app.models.UserConfigView.create(config);
}; };
}; };

View File

@ -29,6 +29,9 @@
"ChatConfig": { "ChatConfig": {
"dataSource": "vn" "dataSource": "vn"
}, },
"DefaultViewConfig": {
"dataSource": "vn"
},
"Delivery": { "Delivery": {
"dataSource": "vn" "dataSource": "vn"
}, },

View File

@ -0,0 +1,25 @@
{
"name": "DefaultViewConfig",
"base": "VnModel",
"options": {
"mysql": {
"table": "salix.defaultViewConfig"
}
},
"properties": {
"tableCode": {
"id": true,
"type": "string",
"required": true
},
"columns": {
"type": "object"
}
},
"acls": [{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
}]
}

View File

@ -0,0 +1,6 @@
CREATE TABLE `salix`.`defaultViewConfig`
(
tableCode VARCHAR(25) not null,
columns JSON not null
)
comment 'The default configuration of columns for views';

View File

@ -2,10 +2,9 @@
<vn-horizontal> <vn-horizontal>
<div class="actions-left"> <div class="actions-left">
<vn-button icon="view_column" <vn-button icon="view_column"
ng-click="$ctrl.shownColumns()" ng-click="smartTableColumns.show($event)"
vn-tooltip="Shown columns"> vn-tooltip="Shown columns">
</vn-button> </vn-button>
<div ng-transclude="actions"></div> <!-- transcluded actions --> <div ng-transclude="actions"></div> <!-- transcluded actions -->
</div> </div>
<div class="actions-right"> <div class="actions-right">
@ -32,7 +31,6 @@
vn-tooltip="Save data"> vn-tooltip="Save data">
</vn-button> </vn-button>
</div> </div>
<div class="button-group"> <div class="button-group">
<vn-button icon="refresh" <vn-button icon="refresh"
ng-click="$ctrl.model.refresh()" ng-click="$ctrl.model.refresh()"
@ -60,4 +58,30 @@
on-accept="$ctrl.deleteAll()" on-accept="$ctrl.deleteAll()"
question="Are you sure you want to continue?" question="Are you sure you want to continue?"
message="Remove selected rows"> message="Remove selected rows">
</vn-confirm> </vn-confirm>
<vn-crud-model
vn-id="userViewModel"
url="UserConfigViews"
link="{tableCode: $ctrl.viewConfigId, userFk: $ctrl.currentUserId}"
data="$ctrl.viewConfig"
auto-load="true">
</vn-crud-model>
<vn-popover class="modal-form" vn-id="smart-table-columns" message="Fields to show">
<tpl-body>
<div class="vn-pa-md">
<vn-horizontal ng-repeat="column in $ctrl.columns">
<vn-check vn-one
label="{{column.caption}}"
ng-model="$ctrl.viewConfig[0].configuration[column.field]">
</vn-check>
</vn-horizontal>
<vn-horizontal>
<vn-button
label="Save"
ng-click="$ctrl.saveViewConfig()">
</vn-button>
</vn-horizontal>
</div>
</tpl-body>
</vn-popover>

View File

@ -2,12 +2,15 @@ import ngModule from '../../module';
import Component from '../../lib/component'; import Component from '../../lib/component';
import {buildFilter} from 'vn-loopback/util/filter'; import {buildFilter} from 'vn-loopback/util/filter';
import './style.scss'; import './style.scss';
import angular from 'angular';
export default class SmartTable extends Component { export default class SmartTable extends Component {
constructor($element, $, $transclude) { constructor($element, $, $transclude) {
super($element, $); super($element, $);
this.currentUserId = window.localStorage.currentUserWorkerId;
this.$transclude = $transclude; this.$transclude = $transclude;
this.sortCriteria = []; this.sortCriteria = [];
this.columns = [];
this.autoSave = false; this.autoSave = false;
} }
@ -23,6 +26,52 @@ export default class SmartTable extends Component {
} }
} }
get viewConfigId() {
return this._viewConfigId;
}
set viewConfigId(value) {
this._viewConfigId = value;
if (value) {
this.defaultViewConfig = {};
const url = 'DefaultViewConfigs';
this.$http.get(url, {where: {tableCode: value}})
.then(res => {
if (res && res.data.length) {
const columns = res.data[0].columns;
this.defaultViewConfig = columns;
}
});
}
}
get viewConfig() {
return this._viewConfig;
}
set viewConfig(value) {
this._viewConfig = value;
if (!value) return;
if (!value.length) {
const userViewModel = this.$.userViewModel;
for (const column of this.columns) {
if (this.defaultViewConfig[column.field] == undefined)
this.defaultViewConfig[column.field] = true;
}
userViewModel.insert({
userFk: this.currentUserId,
tableConfig: this.viewConfigId,
configuration: this.defaultViewConfig ? this.defaultViewConfig : {}
});
}
this.applyViewConfig();
}
get checkedRows() { get checkedRows() {
const model = this.model; const model = this.model;
if (model && model.data) if (model && model.data)
@ -31,16 +80,63 @@ export default class SmartTable extends Component {
return null; return null;
} }
saveViewConfig() {
const userViewModel = this.$.userViewModel;
const [viewConfig] = userViewModel.data;
viewConfig.configuration = Object.assign({}, viewConfig.configuration);
userViewModel.save()
.then(() => this.vnApp.showSuccess(this.$t('Data saved!')))
.then(() => this.applyViewConfig());
}
applyViewConfig() {
const userViewModel = this.$.userViewModel;
const [viewConfig] = userViewModel.data;
const selectors = [];
for (const column of this.columns) {
if (viewConfig.configuration[column.field] == false) {
const baseSelector = `smart-table[view-config-id="${this.viewConfigId}"] table`;
selectors.push(`${baseSelector} thead > tr > th:nth-child(${column.index + 1})`);
selectors.push(`${baseSelector} tbody > tr > td:nth-child(${column.index + 1})`);
}
}
const styleElement = document.querySelector('style[id="smart-table"]');
if (styleElement)
styleElement.parentNode.removeChild(styleElement);
if (selectors.length) {
const rule = selectors.join(', ') + '{display: none}';
this.$.css = document.createElement('style');
this.$.css.setAttribute('id', 'smart-table');
document.head.appendChild(this.$.css);
this.$.css.appendChild(document.createTextNode(rule));
}
this.$.$on('$destroy', () => {
if (this.$.css && styleElement)
styleElement.parentNode.removeChild(styleElement);
});
}
registerColumns() { registerColumns() {
const header = this.element.querySelector('thead > tr'); const header = this.element.querySelector('thead > tr');
if (!header) return; if (!header) return;
const columns = header.querySelectorAll('th'); const columns = header.querySelectorAll('th');
// Click handler // Click handler
for (let column of columns) { for (const [index, column] of columns.entries()) {
const field = column.getAttribute('field'); const field = column.getAttribute('field');
if (field) if (field) {
const columnElement = angular.element(column);
const caption = columnElement.text().trim();
this.columns.push({field, caption, index});
column.addEventListener('click', () => this.orderHandler(column)); column.addEventListener('click', () => this.orderHandler(column));
}
} }
} }
@ -157,7 +253,6 @@ export default class SmartTable extends Component {
} }
filterSanitizer(field) { filterSanitizer(field) {
// tenemos que eliminar ands vacios al ir borrando filtros
const userFilter = this.model.userFilter; const userFilter = this.model.userFilter;
const userParams = this.model.userParams; const userParams = this.model.userParams;
const where = userFilter && userFilter.where; const where = userFilter && userFilter.where;
@ -199,15 +294,17 @@ export default class SmartTable extends Component {
return {userFilter, userParams}; return {userFilter, userParams};
} }
removeFilter(field) { removeFilter() {
//
this.model.applyFilter(userFilter, userParams); this.model.applyFilter(userFilter, userParams);
} }
createRow() { createRow() {
this.model.insert({ let data = {};
nickname: 'New row'
}); if (this.defaultNewData)
data = this.defaultNewData();
this.model.insert(data);
} }
deleteAll() { deleteAll() {
@ -244,7 +341,9 @@ ngModule.vnComponent('smartTable', {
}, },
bindings: { bindings: {
model: '<?', model: '<?',
viewConfigId: '@?',
autoSave: '<?', autoSave: '<?',
exprBuilder: '&?' exprBuilder: '&?',
defaultNewData: '&?'
} }
}); });

View File

@ -1,5 +1,11 @@
<vn-crud-model vn-id="model" url="{{$ctrl.url}}" filter="$ctrl.filter" link="{originFk: $ctrl.originId}" <vn-crud-model
data="$ctrl.logs" limit="20" auto-load="true"> vn-id="model"
url="{{$ctrl.url}}"
filter="$ctrl.filter"
link="{originFk: $ctrl.originId}"
data="$ctrl.logs"
limit="20"
auto-load="true">
</vn-crud-model> </vn-crud-model>
<vn-data-viewer model="model" class="vn-w-xl"> <vn-data-viewer model="model" class="vn-w-xl">
<vn-card> <vn-card>

View File

@ -11,9 +11,10 @@
<!-- To access table component should be a nested component --> <!-- To access table component should be a nested component -->
<smart-table <smart-table
model="model" model="model"
view-config-id="ticketIndex"
auto-save="true" auto-save="true"
expr-builder="$ctrl.exprBuilder(param, value)" expr-builder="$ctrl.exprBuilder(param, value)"
> default-new-data="$ctrl.defaultNewData()">
<slot-actions> <slot-actions>
<vn-button icon="info" <vn-button icon="info"
ng-click="$ctrl.test()" ng-click="$ctrl.test()"

View File

@ -128,6 +128,12 @@ export default class Controller extends Section {
this.$.summary.show(); this.$.summary.show();
} }
defaultNewData() {
return {
nickname: 'my nickname',
};
}
exprBuilder(param, value) { exprBuilder(param, value) {
switch (param) { switch (param) {
case 'nickname': case 'nickname':