mirror of
https://github.com/PanJiaChen/vue-element-admin.git
synced 2025-08-10 20:39:48 +08:00
support Product Configuration (#969)
* support Product Configuration * paint flowchart dynamically * minimal changes Workflow * remove comment Co-authored-by: elsiosanchez <elsiossanches@gmail.com>
This commit is contained in:
parent
07bb5bd692
commit
44ad3521ba
41
src/api/ADempiere/dictionary/workflow.js
Normal file
41
src/api/ADempiere/dictionary/workflow.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// 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'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request dictionary workflow metadata
|
||||||
|
* @param {string} uuid universally unique identifier
|
||||||
|
* @param {number} id, identifier
|
||||||
|
*/
|
||||||
|
export function requestWorkflowMetadata({
|
||||||
|
uuid,
|
||||||
|
id
|
||||||
|
}) {
|
||||||
|
return request({
|
||||||
|
url: '/dictionary/workflow',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
uuid,
|
||||||
|
id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(workflowResponse => {
|
||||||
|
const { convertWorkflow } = require('@/utils/ADempiere/apiConverts/dictionary.js')
|
||||||
|
return convertWorkflow(workflowResponse)
|
||||||
|
})
|
||||||
|
}
|
@ -117,3 +117,29 @@ export function workflowActivities({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// GET Workflows
|
||||||
|
/**
|
||||||
|
* Request Document Status List
|
||||||
|
* @param {string} tableName
|
||||||
|
* @param {number} pageSize
|
||||||
|
* @param {string} pageToken
|
||||||
|
*/
|
||||||
|
export function getWorkflow({
|
||||||
|
tableName,
|
||||||
|
pageSize,
|
||||||
|
pageToken
|
||||||
|
}) {
|
||||||
|
return request({
|
||||||
|
url: '/workflow/workflows',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
table_name: tableName,
|
||||||
|
// Page Data
|
||||||
|
pageToken,
|
||||||
|
pageSize
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(listWorkflowActivities => {
|
||||||
|
return listWorkflowActivities
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -69,7 +69,16 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
getRecordNotification() {
|
getRecordNotification() {
|
||||||
return this.$store.getters.getNotificationProcess
|
return this.$store.getters.getNotificationProcess.map(item => {
|
||||||
|
if (item.typeActivity) {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
name: item.name + ' ' + item.quantityActivities
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -45,37 +45,12 @@
|
|||||||
<el-main class="main">
|
<el-main class="main">
|
||||||
<el-container style="height: 100%;">
|
<el-container style="height: 100%;">
|
||||||
<el-aside v-if="!isEmptyValue(currentActivity)" id="workflow" width="70%" style="background: white;">
|
<el-aside v-if="!isEmptyValue(currentActivity)" id="workflow" width="70%" style="background: white;">
|
||||||
<transition name="el-zoom-in-center">
|
<workflow
|
||||||
<el-card v-show="show" :style="{position: 'absolute', zIndex: '5', left: leftContextualMenu + 'px', top: topContextualMenu + 'px'}" class="box-card">
|
|
||||||
<div slot="header" class="clearfix">
|
|
||||||
<span>
|
|
||||||
{{ infoNode.description }}
|
|
||||||
</span>
|
|
||||||
<el-button style="float: right; padding: 3px 0" type="text" icon="el-icon-close" @click="show = !show" />
|
|
||||||
</div>
|
|
||||||
<div v-if="!isEmptyValue(infoNode.nodeLogs)" class="text item" style="padding: 20px">
|
|
||||||
<el-timeline class="info">
|
|
||||||
<el-timeline-item
|
|
||||||
v-for="(logs, key) in infoNode.nodeLogs"
|
|
||||||
:key="key"
|
|
||||||
:timestamp="translateDate(logs.log_date)"
|
|
||||||
placement="top"
|
|
||||||
>
|
|
||||||
<el-card style="padding: 20px!important;">
|
|
||||||
<b> {{ $t('login.userName') }} </b> {{ logs.user_name }} <br>
|
|
||||||
{{ logs.text_message }}
|
|
||||||
</el-card>
|
|
||||||
</el-timeline-item>
|
|
||||||
</el-timeline>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</transition>
|
|
||||||
<workflow-chart
|
|
||||||
v-if="!isEmptyValue(node) && !isEmptyValue(currentActivity)"
|
v-if="!isEmptyValue(node) && !isEmptyValue(currentActivity)"
|
||||||
:transitions="listWorkflowTransition"
|
:node-transition-list="listWorkflowTransition"
|
||||||
:states="node"
|
:node-list="node"
|
||||||
:state-semantics="currentNode"
|
:current-node="currentNode"
|
||||||
@state-click="onLabelClicked(node, $event)"
|
:workflow-logs="listProcessWorkflow"
|
||||||
/>
|
/>
|
||||||
</el-aside>
|
</el-aside>
|
||||||
<el-main v-if="!isEmptyValue(currentActivity)" style="overflow: hidden;">
|
<el-main v-if="!isEmptyValue(currentActivity)" style="overflow: hidden;">
|
||||||
@ -105,12 +80,12 @@
|
|||||||
<script>
|
<script>
|
||||||
import formMixin from '@/components/ADempiere/Form/formMixin.js'
|
import formMixin from '@/components/ADempiere/Form/formMixin.js'
|
||||||
import fieldsList from './fieldsList.js'
|
import fieldsList from './fieldsList.js'
|
||||||
import WorkflowChart from 'vue-workflow-chart'
|
import Workflow from '@/components/ADempiere/Workflow'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'WorkflowActivity',
|
name: 'WorkflowActivity',
|
||||||
components: {
|
components: {
|
||||||
WorkflowChart
|
Workflow
|
||||||
},
|
},
|
||||||
mixins: [
|
mixins: [
|
||||||
formMixin
|
formMixin
|
||||||
@ -184,9 +159,6 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
activityList(list) {
|
|
||||||
this.SendActivityListNotifier(list)
|
|
||||||
},
|
|
||||||
currentActivity(value) {
|
currentActivity(value) {
|
||||||
this.listWorkflow(value)
|
this.listWorkflow(value)
|
||||||
this.setCurrent()
|
this.setCurrent()
|
||||||
@ -199,9 +171,6 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
SendActivityListNotifier() {
|
|
||||||
this.$store.commit('addNotificationProcess', { name: this.$t('navbar.badge.activity') + ' ' + this.activityList.length, typeActivity: true })
|
|
||||||
},
|
|
||||||
setCurrent() {
|
setCurrent() {
|
||||||
const activity = this.activityList.find(activity => activity.node === this.currentActivity.node)
|
const activity = this.activityList.find(activity => activity.node === this.currentActivity.node)
|
||||||
this.$refs.WorkflowActivity.setCurrentRow(activity)
|
this.$refs.WorkflowActivity.setCurrentRow(activity)
|
||||||
@ -347,27 +316,6 @@ export default {
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.info {
|
|
||||||
margin: 0px;
|
|
||||||
font-size: 14px;
|
|
||||||
list-style: none;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
.vue-workflow-chart-state {
|
|
||||||
background-color: #fff;
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 3px;
|
|
||||||
color: #11353d;
|
|
||||||
font-size: 15px;
|
|
||||||
font-family: Open Sans;
|
|
||||||
/* font-weight: 600; */
|
|
||||||
margin-right: 20px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
max-width: 15%;
|
|
||||||
text-align: center;
|
|
||||||
-webkit-box-shadow: 0 2px 4px 0 rgb(0 0 0 / 20%);
|
|
||||||
box-shadow: 0 2px 4px 0 rgb(0 0 0 / 20%);
|
|
||||||
}
|
|
||||||
.panel_main {
|
.panel_main {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -387,15 +335,3 @@ export default {
|
|||||||
transition: 0.3s;
|
transition: 0.3s;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
@import '~vue-workflow-chart/dist/vue-workflow-chart.css';
|
|
||||||
.vue-workflow-chart-state-delete {
|
|
||||||
color: white;
|
|
||||||
background: #AED5FE;
|
|
||||||
}
|
|
||||||
.vue-workflow-chart-transition-arrow-delete {
|
|
||||||
fill: #AED5FE;
|
|
||||||
}
|
|
||||||
.vue-workflow-chart-transition-path-delete {
|
|
||||||
stroke: #AED5FE;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
173
src/components/ADempiere/Workflow/index.vue
Normal file
173
src/components/ADempiere/Workflow/index.vue
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
<!--
|
||||||
|
ADempiere-Vue (Frontend) for ADempiere ERP & CRM Smart Business Solution
|
||||||
|
Copyright (C) 2017-Present E.R.P. Consultores y Asociados, C.A.
|
||||||
|
Contributor(s): Elsio Sanchez esanchez@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-container style="height: 100% !important;">
|
||||||
|
<el-main style="overflow: hidden;">
|
||||||
|
<transition name="el-zoom-in-bottom">
|
||||||
|
<el-card v-show="show" :style="{position: 'absolute', zIndex: '5', left: leftContextualMenu + 'px', top: topContextualMenu + 'px'}" class="box-card">
|
||||||
|
<div slot="header" class="clearfix">
|
||||||
|
<span>
|
||||||
|
{{ infoNode.description }}
|
||||||
|
</span>
|
||||||
|
<el-button style="float: right; padding: 3px 0" type="text" icon="el-icon-close" @click="show = !show" />
|
||||||
|
</div>
|
||||||
|
<div v-if="!isEmptyValue(infoNode.nodeLogs)" class="text item" style="padding: 20px">
|
||||||
|
<el-timeline class="info">
|
||||||
|
<el-timeline-item
|
||||||
|
v-for="(logs, key) in infoNode.nodeLogs"
|
||||||
|
:key="key"
|
||||||
|
:timestamp="translateDate(logs.log_date)"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
|
<el-card style="padding: 20px!important;">
|
||||||
|
<b> {{ $t('login.userName') }} </b> {{ logs.user_name }} <br>
|
||||||
|
{{ logs.text_message }}
|
||||||
|
</el-card>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</transition>
|
||||||
|
<workflow-chart
|
||||||
|
v-if="!isEmptyValue(nodeList)"
|
||||||
|
id="Diagrama"
|
||||||
|
:transitions="nodeTransitionList"
|
||||||
|
:states="nodeList"
|
||||||
|
:state-semantics="currentNode"
|
||||||
|
@state-click="onLabelClicked(nodeList, $event)"
|
||||||
|
/>
|
||||||
|
</el-main>
|
||||||
|
</el-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import WorkflowChart from 'vue-workflow-chart'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Workflow',
|
||||||
|
components: {
|
||||||
|
WorkflowChart
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
nodeList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
nodeTransitionList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
currentNode: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [{
|
||||||
|
classname: 'delete',
|
||||||
|
id: ''
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
workflowLogs: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
show: false,
|
||||||
|
infoNode: {},
|
||||||
|
topContextualMenu: 0,
|
||||||
|
leftContextualMenu: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onLabelClicked(type, id) {
|
||||||
|
this.infoNode = type.find(node => node.id === id)
|
||||||
|
const nodeLogs = this.workflowLogs.filter(node => node.node_uuid === this.infoNode.uuid)
|
||||||
|
this.infoNode.nodeLogs = nodeLogs
|
||||||
|
const menuMinWidth = 105
|
||||||
|
const offsetLeft = this.$el.getBoundingClientRect().left // container margin left
|
||||||
|
const offsetWidth = this.$el.offsetWidth // container width
|
||||||
|
const maxLeft = offsetWidth - menuMinWidth // left boundary
|
||||||
|
const left = event.clientX - offsetLeft + 15 // 15: margin right
|
||||||
|
|
||||||
|
this.leftContextualMenu = left
|
||||||
|
if (left > maxLeft) {
|
||||||
|
this.leftContextualMenu = maxLeft
|
||||||
|
}
|
||||||
|
|
||||||
|
const offsetTop = this.$el.getBoundingClientRect().top
|
||||||
|
const top = event.clientY - offsetTop + 500
|
||||||
|
this.topContextualMenu = top
|
||||||
|
this.show = true
|
||||||
|
},
|
||||||
|
translateDate(value) {
|
||||||
|
return this.$d(new Date(value), 'long', this.language)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.info {
|
||||||
|
margin: 0px;
|
||||||
|
font-size: 14px;
|
||||||
|
list-style: none;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.vue-workflow-chart-state {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 3px;
|
||||||
|
color: #11353d;
|
||||||
|
font-size: 15px;
|
||||||
|
font-family: Open Sans;
|
||||||
|
/* font-weight: 600; */
|
||||||
|
margin-right: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
max-width: 15%;
|
||||||
|
text-align: center;
|
||||||
|
-webkit-box-shadow: 0 2px 4px 0 rgb(0 0 0 / 20%);
|
||||||
|
box-shadow: 0 2px 4px 0 rgb(0 0 0 / 20%);
|
||||||
|
}
|
||||||
|
.panel_main {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style lang='scss'>
|
||||||
|
.scroll-child {
|
||||||
|
max-height: 450px;
|
||||||
|
}
|
||||||
|
.el-card {
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #e6ebf5;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
overflow: hidden;
|
||||||
|
color: #303133;
|
||||||
|
-webkit-transition: 0.3s;
|
||||||
|
transition: 0.3s;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
@import '~vue-workflow-chart/dist/vue-workflow-chart.css';
|
||||||
|
.vue-workflow-chart-state-delete {
|
||||||
|
color: white;
|
||||||
|
background: #AED5FE;
|
||||||
|
}
|
||||||
|
.vue-workflow-chart-transition-arrow-delete {
|
||||||
|
fill: #AED5FE;
|
||||||
|
}
|
||||||
|
.vue-workflow-chart-transition-path-delete {
|
||||||
|
stroke: #AED5FE;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,7 +1,9 @@
|
|||||||
import {
|
import {
|
||||||
workflowActivities
|
workflowActivities
|
||||||
} from '@/api/ADempiere/workflow.js'
|
} from '@/api/ADempiere/workflow.js'
|
||||||
|
import { isEmptyValue } from '@/utils/ADempiere'
|
||||||
import { showMessage } from '@/utils/ADempiere/notification.js'
|
import { showMessage } from '@/utils/ADempiere/notification.js'
|
||||||
|
import language from '@/lang'
|
||||||
|
|
||||||
const activity = {
|
const activity = {
|
||||||
listActivity: [],
|
listActivity: [],
|
||||||
@ -19,9 +21,11 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
serverListActivity({ commit, getters, rootGetters }) {
|
serverListActivity({ commit, state, dispatch, rootGetters }, params) {
|
||||||
|
const userUuid = isEmptyValue(params) ? rootGetters['user/getUserUuid'] : params
|
||||||
|
const name = language.t('navbar.badge.activity')
|
||||||
workflowActivities({
|
workflowActivities({
|
||||||
userUuid: rootGetters['user/getUserUuid']
|
userUuid
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then(response => {
|
||||||
const { listWorkflowActivities } = response
|
const { listWorkflowActivities } = response
|
||||||
@ -35,6 +39,26 @@ export default {
|
|||||||
showClose: true
|
showClose: true
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
.finally(() => {
|
||||||
|
const notification = rootGetters.getNotificationProcess.find(notification => {
|
||||||
|
if (notification.typeActivity && notification.quantityActivities === state.listActivity.length) {
|
||||||
|
return notification
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (isEmptyValue(notification)) {
|
||||||
|
commit('addNotificationProcess', {
|
||||||
|
name,
|
||||||
|
typeActivity: true,
|
||||||
|
quantityActivities: state.listActivity.length
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
dispatch('updateNotifications', {
|
||||||
|
name,
|
||||||
|
typeActivity: true,
|
||||||
|
quantityActivities: state.listActivity.length
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
selectedActivity({ commit }, activity) {
|
selectedActivity({ commit }, activity) {
|
||||||
commit('setCurrentActivity', activity)
|
commit('setCurrentActivity', activity)
|
||||||
|
@ -783,6 +783,9 @@ const actions = {
|
|||||||
default:
|
default:
|
||||||
executeAction = 'getFieldsFromTab'
|
executeAction = 'getFieldsFromTab'
|
||||||
break
|
break
|
||||||
|
case 'workflow':
|
||||||
|
executeAction = 'getWorkflowFromServer'
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
return dispatch(executeAction, {
|
return dispatch(executeAction, {
|
||||||
|
@ -1220,5 +1220,17 @@ export default {
|
|||||||
dispatch('updateOrderPos', false)
|
dispatch('updateOrderPos', false)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
updateNotifications({ commit, state },
|
||||||
|
update
|
||||||
|
) {
|
||||||
|
const notification = state.notificationProcess.map(notification => {
|
||||||
|
if (notification.name === update.name && notification.typeActivity && notification.quantityActivities !== update.quantityActivities) {
|
||||||
|
return {
|
||||||
|
...update
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
commit('updateNotificationProcess', notification)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,9 @@ export default {
|
|||||||
addNotificationProcess(state, payload) {
|
addNotificationProcess(state, payload) {
|
||||||
state.notificationProcess.push(payload)
|
state.notificationProcess.push(payload)
|
||||||
},
|
},
|
||||||
|
updateNotificationProcess(state, update) {
|
||||||
|
state.notificationProcess = update
|
||||||
|
},
|
||||||
// Delete process in execution afther some response from server
|
// Delete process in execution afther some response from server
|
||||||
deleteInExecution(state, payload) {
|
deleteInExecution(state, payload) {
|
||||||
state.inExecution = state.inExecution.filter(item => item.containerUuid !== payload.containerUuid)
|
state.inExecution = state.inExecution.filter(item => item.containerUuid !== payload.containerUuid)
|
||||||
|
75
src/store/modules/ADempiere/workflowDefinition.js
Normal file
75
src/store/modules/ADempiere/workflowDefinition.js
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import { requestWorkflowMetadata } from '@/api/ADempiere/dictionary/workflow'
|
||||||
|
import { showMessage } from '@/utils/ADempiere/notification'
|
||||||
|
// import router from '@/router'
|
||||||
|
import language from '@/lang'
|
||||||
|
|
||||||
|
const workflow = {
|
||||||
|
state: {
|
||||||
|
workflow: []
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
addWorkflow(state, payload) {
|
||||||
|
state.workflow.push(payload)
|
||||||
|
},
|
||||||
|
dictionaryResetCacheWorkflow(state) {
|
||||||
|
state.workflow = []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
getWorkflowFromServer({ commit, dispatch }, {
|
||||||
|
id,
|
||||||
|
containerUuid,
|
||||||
|
routeToDelete
|
||||||
|
}) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
requestWorkflowMetadata({
|
||||||
|
uuid: containerUuid,
|
||||||
|
id
|
||||||
|
})
|
||||||
|
.then(workflowResponse => {
|
||||||
|
const panelType = 'workflow'
|
||||||
|
|
||||||
|
// Panel for save on store
|
||||||
|
const newWorkflow = {
|
||||||
|
...workflowResponse,
|
||||||
|
containerUuid,
|
||||||
|
fieldsList: [],
|
||||||
|
panelType
|
||||||
|
}
|
||||||
|
|
||||||
|
commit('addWorkflow', newWorkflow)
|
||||||
|
|
||||||
|
resolve(newWorkflow)
|
||||||
|
|
||||||
|
const actions = []
|
||||||
|
|
||||||
|
// Add process menu
|
||||||
|
dispatch('setContextMenu', {
|
||||||
|
containerUuid,
|
||||||
|
actions
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
// router.push({
|
||||||
|
// path: '/dashboard'
|
||||||
|
// }, () => {})
|
||||||
|
// dispatch('tagsView/delView', routeToDelete)
|
||||||
|
showMessage({
|
||||||
|
message: language.t('login.unexpectedError'),
|
||||||
|
type: 'error'
|
||||||
|
})
|
||||||
|
console.warn(`Dictionary Workflow - Error ${error.code}: ${error.message}.`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getters: {
|
||||||
|
getWorkflowUuid: (state) => (workflowUuid) => {
|
||||||
|
return state.workflow.find(
|
||||||
|
item => item.uuid === workflowUuid
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default workflow
|
@ -672,7 +672,6 @@ export function convertAction(action) {
|
|||||||
isIndex: false,
|
isIndex: false,
|
||||||
component: () => import('@/views/ADempiere/Unsupported')
|
component: () => import('@/views/ADempiere/Unsupported')
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case 'B':
|
case 'B':
|
||||||
actionAttributes.name = 'workbech'
|
actionAttributes.name = 'workbech'
|
||||||
@ -681,6 +680,7 @@ export function convertAction(action) {
|
|||||||
case 'F':
|
case 'F':
|
||||||
actionAttributes.name = 'workflow'
|
actionAttributes.name = 'workflow'
|
||||||
actionAttributes.icon = 'example'
|
actionAttributes.icon = 'example'
|
||||||
|
actionAttributes.component = () => import('@/views/ADempiere/Workflow')
|
||||||
break
|
break
|
||||||
case 'P':
|
case 'P':
|
||||||
actionAttributes.name = 'process'
|
actionAttributes.name = 'process'
|
||||||
|
206
src/views/ADempiere/Workflow/index.vue
Normal file
206
src/views/ADempiere/Workflow/index.vue
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
<!--
|
||||||
|
ADempiere-Vue (Frontend) for ADempiere ERP & CRM Smart Business Solution
|
||||||
|
Copyright (C) 2017-Present E.R.P. Consultores y Asociados, C.A.
|
||||||
|
Contributor(s): Edwin Betancourt EdwinBetanc0urt@outlook.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-container class="panel_main">
|
||||||
|
<el-header>
|
||||||
|
<title-and-help
|
||||||
|
:name="workflowFileName"
|
||||||
|
:help="$route.meta.description"
|
||||||
|
/>
|
||||||
|
</el-header>
|
||||||
|
<el-main v-if="isLoadedMetadata">
|
||||||
|
<workflow
|
||||||
|
v-if="!isEmptyValue(node)"
|
||||||
|
:node-transition-list="listWorkflowTransition"
|
||||||
|
:node-list="node"
|
||||||
|
:current-node="currentNode"
|
||||||
|
/>
|
||||||
|
</el-main>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
key="form-loading"
|
||||||
|
v-loading="!isLoadedMetadata"
|
||||||
|
:element-loading-text="$t('notifications.loading')"
|
||||||
|
element-loading-spinner="el-icon-loading"
|
||||||
|
element-loading-background="rgba(255, 255, 255, 0.8)"
|
||||||
|
class="view-loading"
|
||||||
|
/>
|
||||||
|
</el-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// When supporting the workflow, smart browser and reports,
|
||||||
|
// the ContextMenu and sticky must be placed in the layout
|
||||||
|
// import ContextMenu from '@/components/ADempiere/ContextMenu'
|
||||||
|
// import MainPanel from '@/components/ADempiere/Panel'
|
||||||
|
import TitleAndHelp from '@/components/ADempiere/TitleAndHelp'
|
||||||
|
import Workflow from '@/components/ADempiere/Workflow'
|
||||||
|
import { getWorkflow } from '@/api/ADempiere/workflow.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Workflow',
|
||||||
|
components: {
|
||||||
|
Workflow,
|
||||||
|
TitleAndHelp
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
isEdit: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
size: {
|
||||||
|
width: '20px',
|
||||||
|
height: '2px'
|
||||||
|
},
|
||||||
|
workflowMetadata: {},
|
||||||
|
node: [],
|
||||||
|
listWorkflowTransition: [],
|
||||||
|
isLoadedMetadata: false,
|
||||||
|
panelType: 'workflow'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
workflowUuid() {
|
||||||
|
return this.$route.meta.uuid
|
||||||
|
},
|
||||||
|
workflowFileName() {
|
||||||
|
return this.workflowMetadata.fileName || this.$route.meta.title
|
||||||
|
},
|
||||||
|
getWorkflow() {
|
||||||
|
return this.$store.getters.getWorkflowUuid(this.workflowUuid)
|
||||||
|
},
|
||||||
|
nodoWorkflow() {
|
||||||
|
return this.workflowMetadata.node.map(node => {
|
||||||
|
return {
|
||||||
|
id: node.id,
|
||||||
|
label: node.name
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.gettWorkflow()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
gettWorkflow() {
|
||||||
|
const workflow = this.getWorkflow
|
||||||
|
if (workflow) {
|
||||||
|
this.workflowMetadata = workflow
|
||||||
|
this.isLoadedMetadata = true
|
||||||
|
} else {
|
||||||
|
this.$store.dispatch('getPanelAndFields', {
|
||||||
|
containerUuid: this.workflowUuid,
|
||||||
|
panelType: this.panelType,
|
||||||
|
routeToDelete: this.$route
|
||||||
|
}).then(workflowResponse => {
|
||||||
|
this.workflowMetadata = workflowResponse
|
||||||
|
this.listWorkflow(this.workflowMetadata)
|
||||||
|
}).finally(() => {
|
||||||
|
this.isLoadedMetadata = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.serverWorkflow(this.workflowMetadata)
|
||||||
|
},
|
||||||
|
serverWorkflow({ tableName }) {
|
||||||
|
if (this.isEmptyValue(tableName)) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
getWorkflow({
|
||||||
|
tableName
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
this.listWorkflow(response.records)
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.warn(`serverWorkflow: ${error.message}. Code: ${error.code}.`)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
listWorkflow(workflow) {
|
||||||
|
// Highlight Current Node
|
||||||
|
this.transitions = []
|
||||||
|
if (!this.isEmptyValue(workflow.node.uuid)) {
|
||||||
|
this.currentNode = [{
|
||||||
|
classname: 'delete',
|
||||||
|
id: workflow.start_node.uuid
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
const nodes = workflow.workflow_nodes.filter(node => !this.isEmptyValue(node.uuid))
|
||||||
|
this.listNodeTransitions(nodes)
|
||||||
|
if (!this.isEmptyValue(nodes)) {
|
||||||
|
this.node = nodes.map((workflow, key) => {
|
||||||
|
return {
|
||||||
|
...workflow,
|
||||||
|
transitions: workflow.transitions,
|
||||||
|
id: workflow.uuid,
|
||||||
|
key,
|
||||||
|
label: workflow.name
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.node = []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
listNodeTransitions(nodes) {
|
||||||
|
nodes.forEach(element => {
|
||||||
|
const uuid = element.uuid
|
||||||
|
const id = element.value
|
||||||
|
if (!this.isEmptyValue(element.transitions)) {
|
||||||
|
element.transitions.forEach((nextNode, key) => {
|
||||||
|
if (!this.isEmptyValue(nextNode.node_next_uuid)) {
|
||||||
|
if (this.isEmptyValue(nextNode.description)) {
|
||||||
|
this.transitions.push({
|
||||||
|
id: id + key,
|
||||||
|
target: uuid,
|
||||||
|
source: nextNode.node_next_uuid
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.transitions.push({
|
||||||
|
id: id + key,
|
||||||
|
label: nextNode.description,
|
||||||
|
target: uuid,
|
||||||
|
source: nextNode.node_next_uuid
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const blon = nodes.map(item => {
|
||||||
|
return {
|
||||||
|
uuid: item.uuid
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.listWorkflowTransition = this.transitions.filter(data => {
|
||||||
|
const verificar = blon.find(mode => mode.uuid === data.source)
|
||||||
|
if (!this.isEmptyValue(verificar)) {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.panel_main {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
@ -196,6 +196,7 @@ export default {
|
|||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
|
this.$store.dispatch('serverListActivity')
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
console.log('error submit!!')
|
console.log('error submit!!')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user