Support to dynamic dashboard (#219)
* First test for dynamic load * add support to list dashboard items * change some dashboards names Co-authored-by: Yamel Senih <ysenih@erpya.com>pull/3759/head
parent
d622eec48b
commit
f0630d79d6
|
@ -347,6 +347,10 @@ export function requestPrintFormats({ tableName, reportViewUuid, processUuid })
|
||||||
return Instance.call(this).requestPrintFormats({ tableName: tableName, reportViewUuid: reportViewUuid, processUuid: processUuid })
|
return Instance.call(this).requestPrintFormats({ tableName: tableName, reportViewUuid: reportViewUuid, processUuid: processUuid })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function requestLisDashboards(roleUuid) {
|
||||||
|
return Instance.call(this).requestDashboards(roleUuid)
|
||||||
|
}
|
||||||
|
|
||||||
export function requestLanguages() {
|
export function requestLanguages() {
|
||||||
return Instance.call(this).requestLanguages()
|
return Instance.call(this).requestLanguages()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
<template>
|
||||||
|
<component
|
||||||
|
:is="renderDashboard"
|
||||||
|
:ref="dashboard.dashboardName"
|
||||||
|
:metadata="dashboard"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Dashboard',
|
||||||
|
props: {
|
||||||
|
metadata: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dashboard: this.metadata
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// load the component that is indicated in the attributes of received property
|
||||||
|
renderDashboard() {
|
||||||
|
if (this.metadata.fileName === 'userfavorites') {
|
||||||
|
return () => import('@/components/ADempiere/Dashboard/favourites')
|
||||||
|
}
|
||||||
|
return () => import(`@/components/ADempiere/Dashboard/${this.metadata.fileName}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.dashboard-editor-container {
|
||||||
|
padding: 32px;
|
||||||
|
background-color: rgb(240, 242, 245);
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.github-corner {
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
border: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-wrapper {
|
||||||
|
background: #fff;
|
||||||
|
padding: 16px 16px 0;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,156 @@
|
||||||
|
<template>
|
||||||
|
<el-collapse v-model="activeRecentItems" accordion>
|
||||||
|
<el-collapse-item name="recentItems">
|
||||||
|
<template slot="title">
|
||||||
|
<i class="el-icon-time" style="margin-right: 4px;margin-left: 10px;" /> {{ $t('profile.recentItems') }}
|
||||||
|
</template>
|
||||||
|
<el-card class="box-card" :body-style="{ padding: '0px' }" shadow="never">
|
||||||
|
<div class="recent-items">
|
||||||
|
<el-table :data="search.length ? filterResult(search) : recentItems" max-height="455" @row-click="handleClick">
|
||||||
|
<el-table-column width="40">
|
||||||
|
<template slot-scope="{row}">
|
||||||
|
<svg-icon :icon-class="row.icon" class="icon-window" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column>
|
||||||
|
<template slot="header" slot-scope="scope" class="clearfix">
|
||||||
|
<el-input
|
||||||
|
v-model="search"
|
||||||
|
size="mini"
|
||||||
|
:metadata="scope"
|
||||||
|
:placeholder="$t('table.dataTable.search')"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template slot-scope="{row}">
|
||||||
|
<span>{{ row.displayName }}</span>
|
||||||
|
<el-tag class="action-tag">{{ $t(`views.${row.action}`) }}</el-tag>
|
||||||
|
<br>
|
||||||
|
<span class="time">{{ translateDate(row.updated) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-collapse-item>
|
||||||
|
</el-collapse>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getRecentItems as getRecentItemsFromServer } from '@/api/ADempiere'
|
||||||
|
import { convertAction } from '@/utils/ADempiere/dictionaryUtils'
|
||||||
|
export default {
|
||||||
|
name: 'RecentItems',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
activeRecentItems: 'recentItems',
|
||||||
|
recentItems: [],
|
||||||
|
isLoaded: true,
|
||||||
|
search: '',
|
||||||
|
accentRegexp: /[\u0300-\u036f]/g
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
getterRecentItems() {
|
||||||
|
return this.$store.getters.getRecentItems
|
||||||
|
},
|
||||||
|
cachedViews() {
|
||||||
|
return this.$store.getters.cachedViews
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getRecentItems()
|
||||||
|
this.subscribeChanges()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
checkOpened(uuid) {
|
||||||
|
return this.cachedViews.includes(uuid)
|
||||||
|
},
|
||||||
|
getRecentItems() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
getRecentItemsFromServer()
|
||||||
|
.then(response => {
|
||||||
|
const recentItems = response.getRecentitemsList().map(item => {
|
||||||
|
const actionConverted = convertAction(item.getAction())
|
||||||
|
return {
|
||||||
|
action: actionConverted.name,
|
||||||
|
icon: actionConverted.icon,
|
||||||
|
displayName: item.getDisplayname(),
|
||||||
|
menuUuid: item.getMenuuuid(),
|
||||||
|
menuName: item.getMenuname(),
|
||||||
|
windowUuid: item.getWindowuuid(),
|
||||||
|
tableId: item.getTableid(),
|
||||||
|
recordId: item.getRecordid(),
|
||||||
|
uuidRecord: item.getRecorduuid(),
|
||||||
|
tabUuid: item.getTabuuid(),
|
||||||
|
updated: new Date(item.getUpdated()),
|
||||||
|
description: item.getMenudescription()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.recentItems = recentItems
|
||||||
|
this.isLoaded = false
|
||||||
|
resolve(recentItems)
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleClick(row) {
|
||||||
|
if (!this.isEmptyValue(row.uuidRecord)) {
|
||||||
|
this.$router.push({ name: row.menuUuid, query: { action: row.uuidRecord, tabParent: 0 }})
|
||||||
|
} else {
|
||||||
|
this.$router.push({ name: row.menuUuid })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
subscribeChanges() {
|
||||||
|
this.$store.subscribe((mutation, state) => {
|
||||||
|
if (mutation.type === 'notifyDashboardRefresh') {
|
||||||
|
this.getRecentItems()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
filterResult(search) {
|
||||||
|
return this.recentItems.filter(item => this.ignoreAccent(item.displayName).toLowerCase().includes(this.ignoreAccent(search.toLowerCase())))
|
||||||
|
},
|
||||||
|
ignoreAccent(s) {
|
||||||
|
if (!s) { return '' }
|
||||||
|
return s.normalize('NFD').replace(/[\u0300-\u036f]/g, '')
|
||||||
|
},
|
||||||
|
translateDate(value) {
|
||||||
|
return this.$d(new Date(value), 'long', this.language)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.search_recent {
|
||||||
|
width: 50%!important;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
.recent-items {
|
||||||
|
height: 455px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.time {
|
||||||
|
float: left;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
.card-box {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.card-content {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
.icon-window {
|
||||||
|
font-size: x-large;
|
||||||
|
color: #36a3f7;
|
||||||
|
}
|
||||||
|
.action-tag {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,4 +1,5 @@
|
||||||
// Default store for handle dashboard refresh and other functionalities
|
// Default store for handle dashboard refresh and other functionalities
|
||||||
|
import { requestLisDashboards } from '@/api/ADempiere/data'
|
||||||
const dashboard = {
|
const dashboard = {
|
||||||
state: {
|
state: {
|
||||||
dashboard: []
|
dashboard: []
|
||||||
|
@ -14,6 +15,39 @@ const dashboard = {
|
||||||
actions: {
|
actions: {
|
||||||
refreshDashboard({ commit, getters }, parameters) {
|
refreshDashboard({ commit, getters }, parameters) {
|
||||||
commit('notifyDashboardRefresh', parameters)
|
commit('notifyDashboardRefresh', parameters)
|
||||||
|
},
|
||||||
|
listDashboard({ commit }, roleUuid) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
requestLisDashboards(roleUuid)
|
||||||
|
.then(response => {
|
||||||
|
const dashboards = response.getDashboardsList().map(item => {
|
||||||
|
return {
|
||||||
|
windowUuid: item.getWindowuuid(),
|
||||||
|
browserUuid: item.getBrowseruuid(),
|
||||||
|
dashboardName: item.getDashboardname(),
|
||||||
|
dashboardDescription: item.getDashboarddescription(),
|
||||||
|
dashboardHtml: item.getDashboardhtml(),
|
||||||
|
columnNo: item.getColumnno(),
|
||||||
|
lineNo: item.getLineno(),
|
||||||
|
isCollapsible: item.getIscollapsible(),
|
||||||
|
isOpenByDefault: item.getIsopenbydefault(),
|
||||||
|
isEventRequired: item.getIseventrequired(),
|
||||||
|
fileName: item.getFilename()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const roleDashboards = {
|
||||||
|
roleUuid: roleUuid,
|
||||||
|
recordCount: response.getRecordcount(),
|
||||||
|
dashboardList: dashboards,
|
||||||
|
nextPageToken: response.getNextPageToken()
|
||||||
|
}
|
||||||
|
commit('addDashboard', roleDashboards)
|
||||||
|
resolve(roleDashboards)
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
|
@ -21,6 +55,11 @@ const dashboard = {
|
||||||
return state.dashboard.find(
|
return state.dashboard.find(
|
||||||
item => item.uuid === dashboardUuid
|
item => item.uuid === dashboardUuid
|
||||||
)
|
)
|
||||||
|
},
|
||||||
|
getDashboardByRole: (state) => (roleUuid) => {
|
||||||
|
return state.dashboard.find(
|
||||||
|
item => item.roleUuid === roleUuid
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,108 +1,48 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="dashboard-editor-container">
|
<div class="dashboard-editor-container">
|
||||||
<github-corner class="github-corner" />
|
|
||||||
|
|
||||||
<panel-group @handleSetLineChartData="handleSetLineChartData" />
|
|
||||||
|
|
||||||
<el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;">
|
|
||||||
<line-chart :chart-data="lineChartData" />
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<el-row :gutter="32">
|
|
||||||
<el-col :xs="24" :sm="24" :lg="8">
|
|
||||||
<div class="chart-wrapper">
|
|
||||||
<raddar-chart />
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :xs="24" :sm="24" :lg="8">
|
|
||||||
<div class="chart-wrapper">
|
|
||||||
<pie-chart />
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :xs="24" :sm="24" :lg="8">
|
|
||||||
<div class="chart-wrapper">
|
|
||||||
<bar-chart />
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<el-row :gutter="8">
|
<el-row :gutter="8">
|
||||||
<el-col :xs="{span: 24}" :sm="{span: 24}" :md="{span: 24}" :lg="{span: 12}" :xl="{span: 12}" style="padding-right:8px;margin-bottom:30px;">
|
<template v-for="(dashboard, index) in dashboardList">
|
||||||
<recent-items />
|
<el-col :key="index" :xs="{span: 24}" :sm="{span: 24}" :md="{span: 24}" :lg="{span: 12}" :xl="{span: 12}" style="padding-right:8px;margin-bottom:30px;">
|
||||||
</el-col>
|
<dashboard :metadata="dashboard" />
|
||||||
<el-col :xs="{span: 24}" :sm="{span: 24}" :md="{span: 24}" :lg="{span: 12}" :xl="{span: 12}" style="padding-right:8px;margin-bottom:30px;">
|
</el-col>
|
||||||
<favorites />
|
</template>
|
||||||
</el-col>
|
|
||||||
<el-col :xs="{span: 24}" :sm="{span: 12}" :md="{span: 12}" :lg="{span: 6}" :xl="{span: 6}" style="margin-bottom:30px;">
|
|
||||||
<todo-list />
|
|
||||||
</el-col>
|
|
||||||
<el-col :xs="{span: 24}" :sm="{span: 12}" :md="{span: 12}" :lg="{span: 6}" :xl="{span: 6}" style="margin-bottom:30px;">
|
|
||||||
<box-card />
|
|
||||||
</el-col>
|
|
||||||
<el-col :xs="{span: 24}" :sm="{span: 24}" :md="{span: 24}" :lg="{span: 12}" :xl="{span: 12}" style="padding-right:8px;margin-bottom:30px;">
|
|
||||||
<pending-documents />
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import GithubCorner from '@/components/GithubCorner'
|
|
||||||
import PanelGroup from './components/PanelGroup'
|
|
||||||
import LineChart from './components/LineChart'
|
|
||||||
import RaddarChart from './components/RaddarChart'
|
|
||||||
import PieChart from './components/PieChart'
|
|
||||||
import BarChart from './components/BarChart'
|
|
||||||
// import TransactionTable from './components/TransactionTable'
|
|
||||||
import TodoList from './components/TodoList'
|
|
||||||
import BoxCard from './components/BoxCard'
|
|
||||||
import RecentItems from '@/components/ADempiere/RecentItems'
|
|
||||||
import Favorites from '@/components/ADempiere/Favorites'
|
|
||||||
import PendingDocuments from '@/components/ADempiere/PendingDocuments'
|
|
||||||
|
|
||||||
const lineChartData = {
|
import Dashboard from '@/components/ADempiere/Dashboard'
|
||||||
newVisitis: {
|
|
||||||
expectedData: [100, 120, 161, 134, 105, 160, 165],
|
|
||||||
actualData: [120, 82, 91, 154, 162, 140, 145]
|
|
||||||
},
|
|
||||||
messages: {
|
|
||||||
expectedData: [200, 192, 120, 144, 160, 130, 140],
|
|
||||||
actualData: [180, 160, 151, 106, 145, 150, 130]
|
|
||||||
},
|
|
||||||
purchases: {
|
|
||||||
expectedData: [80, 100, 121, 104, 105, 90, 100],
|
|
||||||
actualData: [120, 90, 100, 138, 142, 130, 130]
|
|
||||||
},
|
|
||||||
shoppings: {
|
|
||||||
expectedData: [130, 140, 141, 142, 145, 150, 160],
|
|
||||||
actualData: [120, 82, 91, 154, 162, 140, 130]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DashboardAdmin',
|
name: 'DashboardAdmin',
|
||||||
components: {
|
components: {
|
||||||
GithubCorner,
|
Dashboard
|
||||||
PanelGroup,
|
|
||||||
LineChart,
|
|
||||||
RaddarChart,
|
|
||||||
PieChart,
|
|
||||||
BarChart,
|
|
||||||
// TransactionTable,
|
|
||||||
TodoList,
|
|
||||||
BoxCard,
|
|
||||||
RecentItems,
|
|
||||||
Favorites,
|
|
||||||
PendingDocuments
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
lineChartData: lineChartData.newVisitis
|
roleUuid: this.$store.getters.getRoleUuid,
|
||||||
|
dashboardList: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
getterDashboard() {
|
||||||
|
return this.$store.getters.getDashboardByRole(this.roleUuid)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getDashboardListFromServer()
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleSetLineChartData(type) {
|
getDashboardListFromServer() {
|
||||||
this.lineChartData = lineChartData[type]
|
if (this.getterDashboard) {
|
||||||
|
this.dashboardList = this.getterDashboard
|
||||||
|
} else {
|
||||||
|
this.$store.dispatch('listDashboard', this.roleUuid)
|
||||||
|
.then(response => {
|
||||||
|
this.dashboardList = response.dashboardList
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue