1
0
mirror of https://github.com/PanJiaChen/vue-element-admin.git synced 2025-08-07 18:25:45 +08:00

Feature/#sc add record access api (#794)

* Add API for get Record Access based on server

* support setRecordAcces

* Add mixin for view, this allows reuse code based on unique definition

* support record access

* Support Record Access in Mode Mobile

* minimal changes

Co-authored-by: elsiosanchez <elsiosanche@gmail.com>
This commit is contained in:
Yamel Senih 2021-04-28 19:08:27 -04:00 committed by GitHub
parent 8f178e6dfc
commit 925cf6d679
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 860 additions and 479 deletions

View File

@ -17,10 +17,8 @@
// Get Instance for connection
import { request } from '@/utils/ADempiere/request'
import { convertPrivateAccess } from '@/utils/ADempiere/apiConverts/privateAccess.js'
// Get private access for a record
export function requestGetPrivateAccess({
export function getPrivateAccess({
tableName,
recordId,
recordUuid
@ -35,12 +33,16 @@ export function requestGetPrivateAccess({
}
})
.then(responsePrivateAccess => {
return convertPrivateAccess(responsePrivateAccess)
return {
tableName: responsePrivateAccess.table_name,
recordId: responsePrivateAccess.record_id,
recordUuid: responsePrivateAccess.record_uuid
}
})
}
// Lock a record for a user
export function requestLockPrivateAccess({
export function lockPrivateAccess({
tableName,
recordId,
recordUuid
@ -55,12 +57,16 @@ export function requestLockPrivateAccess({
}
})
.then(responsePrivateAccess => {
return convertPrivateAccess(responsePrivateAccess)
return {
tableName: responsePrivateAccess.table_name,
recordId: responsePrivateAccess.record_id,
recordUuid: responsePrivateAccess.record_uuid
}
})
}
// Unlock a record from a user
export function requestUnlockPrivateAccess({
export function unlockPrivateAccess({
tableName,
recordId,
recordUuid
@ -75,62 +81,10 @@ export function requestUnlockPrivateAccess({
}
})
.then(responsePrivateAccess => {
return convertPrivateAccess(responsePrivateAccess)
})
}
/**
* List Rol Access Record
* @param {string} tableName
* @param {number} recordId
* @param {string} recordUuid
* @param {string} sessionUuid
*/
export function getAccessList({
tableName,
recordId,
recordUuid,
sessionUuid
}) {
return request({
url: '/ui/update-access-record',
method: 'post',
params: {
table_name: tableName,
id: recordId,
uuid: recordUuid,
token: sessionUuid
}
})
.then(respose => {
return respose
})
}
/**
* Update Access Record
* @param {string} tableName
* @param {number} recordId
* @param {string} recordUuid
* @param {array} listRol
*/
export function updateAccessRecord({
tableName,
recordId,
recordUuid,
listRecord
}) {
return request({
url: '/ui/update-access-record',
method: 'post',
params: {
table_name: tableName,
id: recordId,
uuid: recordUuid,
list_rol: listRecord
}
})
.then(response => {
return response
return {
tableName: responsePrivateAccess.table_name,
recordId: responsePrivateAccess.record_id,
recordUuid: responsePrivateAccess.record_uuid
}
})
}

View File

@ -0,0 +1,121 @@
// ADempiere-Vue (Frontend) for ADempiere ERP & CRM Smart Business Solution
// Copyright (C) 2017-Present E.R.P. Consultores y Asociados, C.A.
// Contributor(s): Yamel Senih ysenih@erpya.com www.erpya.com
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// Get Instance for connection
import { request } from '@/utils/ADempiere/request'
/**
* List Rol Access Record
* @param {string} tableName
* @param {number} recordId
* @param {string} recordUuid
* @param {string} sessionUuid
*/
export function getRecordAccess({
tableName,
recordId,
recordUuid
}) {
return new Promise(resolve => {
request({
url: '/ui/record-access',
method: 'get',
params: {
table_name: tableName,
id: recordId,
uuid: recordUuid
}
})
.then(respose => {
resolve(convertRecordAccess(respose))
})
})
}
/**
* Update Access Record
* @param {string} tableName
* @param {number} recordId
* @param {string} recordUuid
* @param {array} listRol
*/
export function setRecordAccess({
recordId,
recordUuid,
tableName,
recordAccesses
}) {
return request({
url: '/ui/set-record-access',
method: 'post',
data: {
table_name: tableName,
id: recordId,
uuid: recordUuid,
record_accesses: recordAccesses.map(access => {
return {
role_id: access.roleId,
role_uuid: access.roleUuid,
role_name: access.roleName,
is_active: access.isActive,
is_exclude: access.isExclude,
is_read_only: access.isReadOnly,
is_dependent_entities: access.isDependentEntities
}
})
}
})
.then(response => {
return response
})
}
/**
* Convert stub from request
* @param {Record Access} recordAccess
* @returns
*/
function convertRecordAccess(recordAccess) {
return {
tableName: recordAccess.table_name,
id: recordAccess.id,
uuid: recordAccess.uuid,
availableRoles: recordAccess.available_roles.map(role => {
return convertRecordAccessRole(role)
}),
currentRoles: recordAccess.current_roles.map(role => {
return convertRecordAccessRole(role)
})
}
}
/**
* Convert role definition
*/
function convertRecordAccessRole(recordAccessRole) {
if (recordAccessRole) {
return {
roleId: recordAccessRole.role_id,
roleUuid: recordAccessRole.role_uuid,
roleName: recordAccessRole.role_name,
isActive: recordAccessRole.is_active,
isExclude: recordAccessRole.is_exclude,
isReadOnly: recordAccessRole.is_read_only,
isDependentEntities: recordAccessRole.is_dependent_entities
}
}
return undefined
}

View File

@ -271,6 +271,7 @@ export default {
} else if (action.action === 'recordAccess') {
this.$store.commit('changeShowRigthPanel', true)
this.$store.commit('setRecordAccess', true)
this.$store.commit('attributeEmbedded', action)
this.runAction(action)
} else {
this.runAction(action)

View File

@ -426,11 +426,6 @@ export default {
}
}
if (this.isWindow && this.isEmptyValue(this.actions.find(element => element.action === 'recordAccess'))) {
this.$store.dispatch('addAttribute', {
tableName: this.tableNameCurrentTab,
recordId: this.getCurrentRecord[this.tableNameCurrentTab + '_ID'],
recordUuid: this.$route.query.action
})
this.actions.push(this.recordAccess)
}

View File

@ -0,0 +1,207 @@
<!--
ADempiere-Vue (Frontend) for ADempiere ERP & CRM Smart Business Solution
Copyright (C) 2017-Present E.R.P. Consultores y Asociados, C.A.
Contributor(s): Yamel Senih ysenih@erpya.com www.erpya.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https:www.gnu.org/licenses/>.
-->
<template>
<el-dialog
:title="attributeEmbedded.name"
:visible="isVisibleDialog"
show-close
:before-close="closeDialog"
:width="width + '%'"
top="5vh"
close-on-press-escape
close-on-click-modal
>
<slot />
</el-dialog>
</template>
<script>
import { showNotification } from '@/utils/ADempiere/notification'
export default {
name: 'Embedded',
props: {
parentUuid: {
type: String,
default: undefined
},
containerUuid: {
type: String,
default: ''
},
panelType: {
type: String,
default: 'window'
},
reportExportType: {
type: String,
default: ''
},
tableName: {
type: String,
default: undefined
},
recordId: {
type: Object,
default: undefined
}
},
computed: {
isMobile() {
return this.$store.state.app.device === 'mobile'
},
width() {
if (this.isMobile) {
return 80
}
return 70
},
attributeEmbedded() {
return this.$store.getters.getAttributeEmbedded
},
isVisibleDialog() {
return this.$store.state['process/index'].isVisibleDialog
},
modalMetadata() {
return this.$store.state['process/index'].metadata
},
windowRecordSelected() {
return this.$store.state['windowControl/index'].recordSelected
},
getterDataRecordsAndSelection() {
return this.$store.getters.getDataRecordAndSelection(this.containerUuid)
},
showRecordAccess() {
return this.$store.getters.getShowRecordAccess
}
},
watch: {
isVisibleDialog(value) {
if (value) {
if (this.modalMetadata.isSortTab) {
const data = this.$store.getters.getDataRecordAndSelection(this.modalMetadata.containerUuid)
if (!data.isLoaded && !data.record.length) {
this.$store.dispatch('getDataListTab', {
parentUuid: this.modalMetadata.parentUuid,
containerUuid: this.modalMetadata.containerUuid,
isAddRecord: true
})
.catch(error => {
console.warn(`Error getting data list tab. Message: ${error.message}, code ${error.code}.`)
})
}
}
}
}
},
methods: {
showNotification,
closeDialog() {
this.$store.dispatch('setShowDialog', {
type: this.modalMetadata.panelType,
action: {
name: ''
}
})
this.$store.commit('setRecordAccess', false)
},
runAction(action) {
this.$store.commit('setRecordAccess', false)
if (action.isSortTab) {
this.$store.dispatch('updateSequence', {
parentUuid: this.modalMetadata.parentUuid,
containerUuid: this.modalMetadata.containerUuid
})
return
}
if (action === undefined && this.windowRecordSelected !== undefined) {
this.$router.push({
name: this.$route.name,
query: {
...this.$route.query,
action: this.windowRecordSelected.UUID
}
}, () => {})
this.closeDialog()
} else if (!this.isEmptyValue(action)) {
const fieldNotReady = this.$store.getters.isNotReadyForSubmit(action.uuid)
if (this.panelType === 'From') {
this.$store.dispatch('processPos', {
action: action, // process metadata
parentUuid: this.parentUuid,
idProcess: this.$store.getters.posAttributes.currentOrder.id,
containerUuid: this.containerUuid,
panelType: this.panelType, // determinate if get table name and record id (window) or selection (browser)
parametersList: this.$store.getters.getPosParameters
})
.catch(error => {
console.warn(error)
})
this.closeDialog()
} else {
if (!fieldNotReady) {
this.closeDialog()
const porcesTabla = this.$store.getters.getProcessSelect.processTablaSelection
const selection = this.$store.getters.getProcessSelect
if (porcesTabla) {
// manage excecute process with records selection
this.$store.dispatch('selectionProcess', {
action: action, // process metadata
parentUuid: this.parentUuid,
containerUuid: this.containerUuid,
panelType: this.panelType, // determinate if get table name and record id (window) or selection (browser)
reportFormat: this.reportExportType,
recordUuidSelection: selection,
isProcessTableSelection: true,
routeToDelete: this.$route
})
} else {
this.$store.dispatch('startProcess', {
action: action, // process metadata
parentUuid: this.parentUuid,
isProcessTableSelection: false,
containerUuid: this.containerUuid,
panelType: this.panelType, // determinate if get table name and record id (window) or selection (browser)
reportFormat: this.reportExportType,
routeToDelete: this.$route
})
.catch(error => {
console.warn(error)
})
}
} else {
this.showNotification({
type: 'warning',
title: this.$t('notifications.emptyValues'),
name: '<b>' + fieldNotReady.name + '.</b> ',
message: this.$t('notifications.fieldMandatory')
})
}
}
}
}
}
}
</script>
<style>
.el-dialog__body {
padding: 10px 20px;
max-height: 65vh;
overflow: auto;
}
</style>

View File

@ -17,6 +17,7 @@
-->
<template>
<el-dialog
v-if="!showRecordAccess"
:title="modalMetadata.name"
:visible="isVisibleDialog"
show-close
@ -30,9 +31,6 @@
<div
v-if="panelType !== 'From'"
>
<record-access
v-if="showRecordAccess"
/>
<sequence-order
v-if="modalMetadata.isSortTab"
key="order"
@ -54,12 +52,13 @@
/>
</template>
</div>
<span slot="footer" class="dialog-footer">
<span v-if="!showRecordAccess" slot="footer" class="dialog-footer">
<el-button
type="danger"
icon="el-icon-close"
@click="closeDialog"
/>
alooooooooo
<el-button
type="primary"
icon="el-icon-check"
@ -72,17 +71,13 @@
<script>
import MainPanel from '@/components/ADempiere/Panel'
import SequenceOrder from '@/components/ADempiere/SequenceOrder'
import RecordAccess from '@/components/ADempiere/RecordAccess'
import { showNotification } from '@/utils/ADempiere/notification'
import {
updateAccessRecord
} from '@/api/ADempiere/private-access'
export default {
name: 'ModalProcess',
components: {
MainPanel,
SequenceOrder,
RecordAccess
SequenceOrder
},
props: {
parentUuid: {
@ -110,7 +105,7 @@ export default {
if (this.isMobile) {
return 80
}
return 50
return 80
},
isVisibleDialog() {
return this.$store.state['process/index'].isVisibleDialog
@ -232,7 +227,8 @@ export default {
}
if (action.action === undefined) {
const list = this.$store.getters.getListRecordAcces
updateAccessRecord(list)
// updateAccessRecord(list)
console.log(list)
}
}
}

View File

@ -1,12 +1,24 @@
<template>
<component
:is="templateDevice"
:record="record"
:table-name="tableName"
/>
</template>
<script>
export default {
name: 'RecordAccess',
props: {
record: {
type: Object,
default: () => {}
},
tableName: {
type: String,
default: undefined
}
},
computed: {
isMobile() {
return this.$store.state.app.device === 'mobile'

View File

@ -0,0 +1,193 @@
// ADempiere-Vue (Frontend) for ADempiere ERP & CRM Smart Business Solution
// Copyright (C) 2017-Present E.R.P. Consultores y Asociados, C.A.
// Contributor(s): Yamel Senih ysenih@erpya.com www.erpya.com
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import { getRecordAccess, setRecordAccess } from '@/api/ADempiere/actions/record-access.js'
import { showMessage } from '@/utils/ADempiere/notification.js'
import language from '@/lang'
export default {
name: 'MixinRecordAccess',
props: {
parentUuid: {
type: String,
default: undefined
},
containerUuid: {
type: String,
default: undefined
},
order: {
type: String,
default: undefined
},
included: {
type: String,
default: undefined
},
keyColumn: {
type: String,
default: undefined
},
identifiersList: {
type: Array,
default: undefined
},
record: {
type: Object,
default: () => {}
},
tableName: {
type: String,
default: undefined
}
},
data() {
return {
group: 'sequence',
isReadonly: false,
isDependentEntities: true,
recordAccess: {
recordUuid: '',
roles: []
}
}
},
computed: {
excludedList: {
get() {
if (this.recordAccess.roles) {
return this.recordAccess.roles.filter(role => !role.isLocked)
} else {
return []
}
},
set(value) {
}
},
includedList: {
get() {
if (this.recordAccess.roles) {
return this.recordAccess.roles.filter(role => role.isLocked)
} else {
return []
}
},
set(value) {
}
},
getIdentifiersList() {
return this.identifiersList
.filter(item => item.componentPath !== 'FieldSelect')
}
},
created() {
getRecordAccess({
tableName: this.tableName,
recordId: this.record[this.tableName + '_ID'],
recordUuid: this.record.UUID
})
.then(access => {
this.recordAccess.tableName = access.tableName
this.recordAccess.recordId = access.recordId
this.recordAccess.recordUuid = access.recordUuid
access.availableRoles.forEach(role => {
this.recordAccess.roles.push({
...role,
isLocked: false
})
})
access.currentRoles.forEach(role => {
this.recordAccess.roles.find(availableRole => availableRole.roleId === role.roleId).isLocked = true
this.recordAccess.roles.find(availableRole => availableRole.roleId === role.roleId).isDependentEntities = role.isDependentEntities
this.recordAccess.roles.find(availableRole => availableRole.roleId === role.roleId).isReadOnly = role.isReadOnly
})
})
},
methods: {
handleChange(value) {
const action = Object.keys(value)[0] // get property
const element = value[action].element
const index = this.recordAccess.roles.findIndex(role => role.roleId === element.roleId)
switch (action) {
case 'added':
this.addItem({
index,
element
})
break
case 'removed':
this.deleteItem({
index,
element
})
break
}
},
/**
* @param {number} index: the index of the added element
* @param {object} element: the added element
*/
addItem({
index,
element
}) {
this.recordAccess.roles[index].isLocked = true
},
/**
* @param {number} index: the index of the element before remove
* @param {object} element: the removed element
*/
deleteItem({
index,
element
}) {
this.recordAccess.roles[index].isLocked = false
},
getOrder(arrayToSort, orderBy = this.order) {
return arrayToSort.sort((itemA, itemB) => {
return itemA[orderBy] - itemB[orderBy]
})
},
saveRecordAccess(recordAccesses) {
setRecordAccess({
tableName: this.tableName,
recordId: this.record[this.tableName + '_ID'],
recordUuid: this.record.UUID,
recordAccesses
})
.then(response => {
showMessage({
message: language.t('grid.recordAccess.accessGranted')
})
this.close()
})
.catch(error => {
showMessage({
message: error.message,
type: 'error'
})
console.warn(`setPreference error: ${error.message}.`)
})
},
close() {
this.$store.dispatch('setShowDialog', {
type: 'window',
action: undefined
})
this.$store.commit('setRecordAccess', false)
this.$store.commit('changeShowRigthPanel', false)
}
}
}

View File

@ -16,196 +16,122 @@
along with this program. If not, see <https:www.gnu.org/licenses/>.
-->
<template>
<div class="board">
<div
:key="1"
class="kanban todo"
header-text="Todo"
>
<div class="board-column">
<div class="board-column-header">
{{ $t('data.recordAccess.hideRecord') }} ({{ getterListExclude.length }})
</div>
<draggable
v-model="getterListExclude"
:group="group"
v-bind="$attrs"
class="board-column-content"
>
<div
v-for="element in getterListExclude"
:key="element.UUID"
class="board-item"
>
{{ element.clientName }}
<div>
<div class="board">
<div
:key="1"
class="kanban todo"
header-text="Todo"
style="padding: 0px;margin: 0px;width: 35%;padding-right: 3%;"
>
<div class="board-column">
<div class="board-column-header">
{{ $t('data.recordAccess.hideRecord') }} ({{ excludedList.length }})
</div>
<draggable
v-model="excludedList"
:group="group"
v-bind="$attrs"
class="board-column-content"
>
<div
v-for="(element, index) in excludedList"
:key="element.roleUuid"
class="board-item"
style="height: 50%;padding-left: 0px;padding-right: 0px;"
>
<el-table
v-if="!isEmptyValue(excludedList)"
:data="[excludedList[index]]"
border
:show-header="false"
>
<el-table-column
prop="roleName"
/>
</el-table>
</div>
</draggable>
</div>
</div>
<div
:key="2"
class="kanban working"
header-text="Working"
>
<div class="board-column">
<div class="board-column-header">
{{ $t('data.recordAccess.recordDisplay') }} {{ getterListInclude.length }}
</draggable>
</div>
<draggable
v-model="getterListInclude"
:group="group"
v-bind="$attrs"
class="board-column-content"
@change="handleChange"
>
<div
v-for="element in getterListInclude"
:key="element.UUID"
class="board-item"
>
{{ element.name }}
<el-divider direction="vertical" />
{{ $t('data.recordAccess.isReadonly') }} <el-checkbox v-model="isReadonly" />
<el-divider direction="vertical" />
{{ $t('data.recordAccess.isDependentEntities') }} <el-checkbox v-model="isDependentEntities" />
</div>
<div
:key="2"
class="kanban working"
header-text="Working"
style="padding: 0px;margin: 0px;width: 65%;padding-right: 3%;"
>
<div class="board-column">
<div class="board-column-header">
{{ $t('data.recordAccess.recordDisplay') }} ({{ includedList.length }})
</div>
</draggable>
<draggable
v-model="includedList"
:group="group"
v-bind="$attrs"
class="board-column-content"
@change="handleChange"
>
<div
v-for="(element, index) in includedList"
:key="element.roleUuid"
class="board-item"
style="height: 50%;padding-left: 0px;padding-right: 0px;"
>
<el-table
v-if="!isEmptyValue(includedList)"
:data="[includedList[index]]"
border
:show-header="false"
>
<el-table-column
prop="roleName"
min-width="150"
/>
<el-table-column
fixed="right"
>
<template slot-scope="scope">
{{ $t('data.recordAccess.isReadonly') }} <el-switch v-model="scope.row.isReadOnly" />
</template>
</el-table-column>
<el-table-column
fixed="right"
>
<template slot-scope="scope">
{{ $t('data.recordAccess.isDependentEntities') }} <el-switch v-model="scope.row.isDependentEntities" />
</template>
</el-table-column>
</el-table>
</div>
</draggable>
</div>
</div>
</div>
<span slot="footer" class="dialog-footer" style="float: right;padding-top: 1%;">
<el-button
type="danger"
icon="el-icon-close"
@click="close"
/>
<el-button
type="primary"
icon="el-icon-check"
@click="saveRecordAccess(includedList)"
/>
</span>
</div>
</template>
<script>
import draggable from 'vuedraggable'
import recordAccessMixin from './recordAccess.js'
export default {
name: 'RecordAccessDesktop',
components: {
draggable
},
props: {
parentUuid: {
type: String,
default: undefined
},
containerUuid: {
type: String,
default: undefined
},
order: {
type: String,
default: undefined
},
included: {
type: String,
default: undefined
},
keyColumn: {
type: String,
default: undefined
},
identifiersList: {
type: Array,
default: undefined
}
},
data() {
return {
group: 'sequence',
isReadonly: false,
isDependentEntities: true,
getterDataRecords: this.$store.getters['user/getRoles']
}
},
computed: {
getterListExclude: {
get() {
return this.getterDataRecords.filter(item => item.isPersonalLock === false)
},
set(value) {
}
},
getterListInclude: {
get() {
return this.getterDataRecords.filter(item => item.isPersonalLock === true)
},
set(value) {
}
},
getIdentifiersList() {
return this.identifiersList
.filter(item => item.componentPath !== 'FieldSelect')
}
},
created() {
const record = this.getterDataRecords.map(record => {
return {
id: record.id,
uuid: record.uuid,
IsExclude: record.isPersonalLock,
isDependentEntities: this.isDependentEntities,
isReadonly: this.isReadonly
}
})
this.$store.dispatch('changeList', record)
},
methods: {
handleChange(value) {
const action = Object.keys(value)[0] // get property
const element = value.[action].element
const index = this.getterDataRecords.findIndex(role => role.id === element.id)
switch (action) {
case 'added':
this.addItem({
index,
element
})
break
case 'removed':
this.deleteItem({
index,
element
})
break
}
},
/**
* @param {number} index: the index of the added element
* @param {object} element: the added element
*/
addItem({
index,
element
}) {
this.getterDataRecords[index].isPersonalLock = !element.isPersonalLock
},
/**
* @param {number} index: the index of the element before remove
* @param {object} element: the removed element
*/
deleteItem({
index,
element
}) {
this.getterDataRecords[index].isPersonalLock = !element.isPersonalLock
const record = this.getterDataRecords.map(record => {
return {
id: record.id,
uuid: record.uuid,
IsExclude: record.isPersonalLock,
isDependentEntities: this.isDependentEntities,
isReadonly: this.isReadonly
}
})
this.$store.dispatch('changeList', record)
},
getOrder(arrayToSort, orderBy = this.order) {
return arrayToSort.sort((itemA, itemB) => {
return itemA[orderBy] - itemB[orderBy]
})
}
}
mixins: [recordAccessMixin]
}
</script>
@ -255,7 +181,28 @@ export default {
}
</style>
<style lang="scss">
.board-column .board-column-content[data-v-67e5b2d0] {
max-height: 350px;
height: auto;
overflow: auto;
border: 10px solid transparent;
min-height: 60px;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: start;
-ms-flex-pack: start;
justify-content: flex-start;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.board {
height: 400px;
width: 100%;
margin-left: 20px;
display: flex;

View File

@ -1,86 +1,99 @@
<template>
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>
{{ $t('data.recordAccess.actions') }}
</span>
</div>
<div style="margin-bottom: 10%;">
<span style="margin-bottom: 10%;">
{{ $t('data.recordAccess.hideRecord') }}
</span>
<br>
<el-select
v-model="getterListExclude"
multiple
style="margin-top: 5%;"
placeholder="Select"
clearable
@change="addListExclude"
>
<el-option
v-for="item in getterDataRecords"
:key="item.clientId"
:label="item.name"
:value="item.uuid"
/>
</el-select>
</div>
<div
style="margin-bottom: 10%;"
>
<span
<div>
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>
{{ $t('data.recordAccess.actions') }}
</span>
</div>
<div style="margin-bottom: 10%;">
<span style="margin-bottom: 10%;">
{{ $t('data.recordAccess.hideRecord') }} ({{ labelListExcludo.length }})
</span>
<br>
<el-select
v-model="labelListExcludo"
multiple
style="margin-top: 5%;"
filterable
placeholder="Select"
collapse-tags
@change="addListExclude"
>
<el-option
v-for="item in includedList"
:key="item.roleUuid"
:label="item.roleName"
:value="item.roleName"
/>
</el-select>
</div>
<div
style="margin-bottom: 10%;"
>
{{ $t('data.recordAccess.recordDisplay') }}
</span>
<br>
<el-select
v-model="getterListInclude"
multiple
style="margin-top: 5%;"
placeholder="Select"
@change="addListInclude"
>
<el-option
v-for="item in getterDataRecords"
:key="item.clientId"
:label="item.name"
:value="item.uuid"
/>
</el-select>
</div>
<el-form
label-position="top"
size="small"
class="create-bp"
>
<el-row :gutter="24">
<template
v-for="(record, index) in getterListInclude"
<span
style="margin-bottom: 10%;"
>
<div :key="index" style="margin-left: 5%;">
<el-tag>
{{ record }}
</el-tag>
<p>
{{ $t('data.recordAccess.isReadonly') }}
<el-checkbox v-model="isReadonly" />
</p>
<p>
{{ $t('data.recordAccess.isDependentEntities') }} <el-checkbox v-model="isDependentEntities" />
</p>
</div>
</template>
</el-row>
</el-form>
</el-card>
{{ $t('data.recordAccess.recordDisplay') }} ({{ labelListInclude.length }})
</span>
<br>
<el-select
v-model="labelListInclude"
multiple
style="margin-top: 5%;"
placeholder="Select"
filterable
collapse-tags
@change="addListInclude"
>
<el-option
v-for="item in excludedList"
:key="item.roleUuid"
:label="item.roleName"
:value="item.roleName"
/>
</el-select>
</div>
<el-form
label-position="top"
size="small"
class="create-bp"
>
<el-scrollbar wrap-class="scroll-panel-right-mode-mobile" style="max-height: 200px;">
<el-row :gutter="24">
<div style="margin-left: 5%;">
<p>
{{ $t('data.recordAccess.isReadonly') }} <el-switch v-model="isReadonly" />
</p>
<p>
{{ $t('data.recordAccess.isDependentEntities') }} <el-switch v-model="isDependentEntities" />
</p>
</div>
</el-row>
</el-scrollbar>
</el-form>
</el-card>
<span style="float: right;padding-top: 1%;">
<el-button
type="danger"
icon="el-icon-close"
@click="close"
/>
<el-button
type="primary"
icon="el-icon-check"
@click="SendRecorAccess(includedList)"
/>
</span>
</div>
</template>
<script>
import recordAccessMixin from './recordAccess.js'
export default {
name: 'RecordAccessMobile',
mixins: [recordAccessMixin],
props: {
parentUuid: {
type: String,
@ -111,102 +124,64 @@ export default {
return {
group: 'sequence',
isReadonly: false,
isDependentEntities: true,
getterDataRecords: this.$store.getters['user/getRoles']
isDependentEntities: false,
labelListInclude: [],
labelListExcludo: []
}
},
computed: {
getterListExclude() {
const list = this.getterDataRecords.filter(item => item.isPersonalLock === false)
return list.map(element => {
return element.name
listExclude() {
return this.excludedList.map(element => {
return element.roleName
})
},
getterListInclude() {
const list = this.getterDataRecords.filter(item => item.isPersonalLock === true)
return list.map(element => {
return element.name
listInclude() {
return this.includedList.map(element => {
return element.roleName
})
},
getIdentifiersList() {
return this.identifiersList
.filter(item => item.componentPath !== 'FieldSelect')
}
},
created() {
const record = this.getterDataRecords.map(record => {
return {
id: record.id,
uuid: record.uuid,
IsExclude: record.isPersonalLock,
isDependentEntities: this.isDependentEntities,
isReadonly: this.isReadonly
}
})
this.$store.dispatch('changeList', record)
watch: {
listInclude(value) {
this.labelListInclude = value
},
listExclude(value) {
this.labelListExcludo = value
}
},
methods: {
handleChange(value) {
const action = Object.keys(value)[0] // get property
const element = value.[action].element
const index = this.getterDataRecords.findIndex(role => role.id === element.id)
switch (action) {
case 'added':
this.addItem({
index,
element
})
break
case 'removed':
this.deleteItem({
index,
element
})
break
}
},
addListInclude(element) {
const index = this.getterDataRecords.findIndex(item => element[element.length - 1] === item.uuid)
this.getterDataRecords[index].isPersonalLock = true
},
addListExclude(element) {
const index = this.getterDataRecords.findIndex(item => element[element.length - 1] === item.uuid)
this.getterDataRecords[index].isPersonalLock = false
},
/**
* @param {number} index: the index of the added element
* @param {object} element: the added element
*/
addItem({
index,
element
}) {
this.getterDataRecords[index].isPersonalLock = !element.isPersonalLock
},
/**
* @param {number} index: the index of the element before remove
* @param {object} element: the removed element
*/
deleteItem({
index,
element
}) {
this.getterDataRecords[index].isPersonalLock = !element.isPersonalLock
const record = this.getterDataRecords.map(record => {
return {
id: record.id,
uuid: record.uuid,
IsExclude: record.isPersonalLock,
isDependentEntities: this.isDependentEntities,
isReadonly: this.isReadonly
const index = this.recordAccess.roles.findIndex(item => {
if (element[element.length - 1] === item.roleName) {
return item
}
})
this.$store.dispatch('changeList', record)
if (index >= 0) {
this.addItem({
index,
element: this.recordAccess.roles[index]
})
}
},
getOrder(arrayToSort, orderBy = this.order) {
return arrayToSort.sort((itemA, itemB) => {
return itemA[orderBy] - itemB[orderBy]
addListExclude(element) {
const index = this.recordAccess.roles.findIndex(item => {
if (element[element.length - 1] === item.roleName) {
return item
}
})
if (index >= 0) {
this.deleteItem({
index,
element: this.recordAccess.roles[index]
})
}
},
SendRecorAccess(list) {
list.forEach(element => {
element.isReadOnly = this.isReadonly
element.isDependentEntities = this.isDependentEntities
})
this.saveRecordAccess(list)
}
}
}
@ -258,6 +233,15 @@ export default {
}
</style>
<style lang="scss">
.scroll-panel-right-mode-mobile {
max-height: 200px;
overflow-y: auto;
overflow-x: hidden;
}
.el-scrollbar__bar.is-vertical > div {
width: 100%;
transform: translateY(998%);
}
.board {
width: 100%;
margin-left: 20px;

View File

@ -170,6 +170,11 @@ export default {
allWindows: ' and all Windows'
}
},
grid: {
recordAccess: {
accessGranted: 'Access granted for roles'
}
},
views: {
browser: 'Smart Browser',
smartBrowser: 'Smart Browser',

View File

@ -170,6 +170,11 @@ export default {
allWindows: ' y todas las Ventanas'
}
},
grid: {
recordAccess: {
accessGranted: 'Acceso otorgado a los roles'
}
},
views: {
browser: 'Consulta Inteligente',
smartBrowser: 'Consulta Inteligente',

View File

@ -1,38 +0,0 @@
const initStateUtils = {
listRecordAcces: [],
attribute: {}
}
export default {
state: initStateUtils,
mutations: {
setListRecordAcces(state, listRecordAcces) {
state.listRecordAcces = listRecordAcces
},
setAttribute(state, attribute) {
state.attribute = attribute
}
},
actions: {
changeList({ commit, state }, listRecord) {
const recordAccess = {
recordId: state.attribute.recordId,
recordUuid: state.attribute.recordUuid,
tableName: state.attribute.tableName,
listRecord
}
commit('setListRecordAcces', recordAccess)
},
addAttribute({ commit }, params) {
commit('setAttribute', params)
}
},
getters: {
getListRecordAcces: (state) => {
return state.listRecordAcces
},
getAttribute: (state) => {
return state.attribute
}
}
}

View File

@ -29,7 +29,8 @@ const initStateContextMenu = {
recordId: undefined,
recordUuid: undefined
},
recordAccess: false
recordAccess: false,
embedded: {}
}
const contextMenu = {
@ -61,6 +62,9 @@ const contextMenu = {
},
setRecordAccess(state, params) {
state.recordAccess = params
},
attributeEmbedded(state, params) {
state.embedded = params
}
},
actions: {
@ -196,6 +200,9 @@ const contextMenu = {
},
getShowRecordAccess: (state) => {
return state.recordAccess
},
getAttributeEmbedded: (state) => {
return state.embedded
}
}
}

View File

@ -8,10 +8,10 @@ import {
requestGetContextInfoValue
} from '@/api/ADempiere/values'
import {
requestGetPrivateAccess,
requestLockPrivateAccess,
requestUnlockPrivateAccess
} from '@/api/ADempiere/private-access'
getPrivateAccess,
lockPrivateAccess,
unlockPrivateAccess
} from '@/api/ADempiere/actions/private-access'
import {
extractPagingToken,
isEmptyValue
@ -757,7 +757,7 @@ const actions = {
recordId,
recordUuid
}) {
return requestGetPrivateAccess({
return getPrivateAccess({
tableName,
recordId,
recordUuid
@ -786,7 +786,7 @@ const actions = {
recordId,
recordUuid
}) {
return requestLockPrivateAccess({
return lockPrivateAccess({
tableName,
recordId,
recordUuid
@ -820,7 +820,7 @@ const actions = {
recordId,
recordUuid
}) {
return requestUnlockPrivateAccess({
return unlockPrivateAccess({
tableName,
recordId,
recordUuid

View File

@ -1,23 +0,0 @@
// ADempiere-Vue (Frontend) for ADempiere ERP & CRM Smart Business Solution
// Copyright (C) 2017-Present E.R.P. Consultores y Asociados, C.A.
// Contributor(s): Yamel Senih ysenih@erpya.com www.erpya.com
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
export function convertPrivateAccess(privateAccessToConvert) {
return {
tableName: privateAccessToConvert.table_name,
recordId: privateAccessToConvert.record_id,
recordUuid: privateAccessToConvert.record_uuid
}
}

View File

@ -184,9 +184,20 @@
</div>
</div>
<modal-dialog
v-if="!showRecordAccess"
:parent-uuid="windowUuid"
:container-uuid="windowMetadata.currentTabUuid"
/>
<embedded
v-else
:parent-uuid="windowUuid"
:container-uuid="windowMetadata.currentTabUuid"
>
<record-access
:table-name="getTableName"
:record="getRecord"
/>
</embedded>
<div class="small-4 columns">
<div class="w">
<div class="open-left" />
@ -316,6 +327,8 @@
>
<record-access
v-if="showRecordAccess"
:table-name="getTableName"
:record="getRecord"
/>
<component
:is="componentRender"

View File

@ -20,6 +20,7 @@ import TabChildren from '@/components/ADempiere/Tab/tabChildren'
// the submenu and sticky must be placed in the layout
import ContextMenu from '@/components/ADempiere/ContextMenu'
import ModalDialog from '@/components/ADempiere/Dialog'
import Embedded from '@/components/ADempiere/Dialog/embedded'
import DataTable from '@/components/ADempiere/DataTable'
import splitPane from 'vue-splitpane'
// Container Info
@ -52,7 +53,8 @@ export default {
RecordLogs,
WorkflowLogs,
WorkflowStatusBar,
RecordAccess
RecordAccess,
Embedded
},
beforeRouteUpdate(to, from, next) {
this.$store.dispatch('setWindowOldRoute', {