WIP: #258 Compare page
This commit is contained in:
@@ -182,7 +182,7 @@ export function followTimeLabel(second, max, fixedNumber = 0) {
|
|||||||
result = (second / day).toFixed(fixedNumber) + 'd';
|
result = (second / day).toFixed(fixedNumber) + 'd';
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
if(((second % day) / hour) === 0) {
|
if((second / hour) === 0) {
|
||||||
fixedNumber = 0;
|
fixedNumber = 0;
|
||||||
}
|
}
|
||||||
result = (second / hour).toFixed(fixedNumber) + 'h';
|
result = (second / hour).toFixed(fixedNumber) + 'h';
|
||||||
|
|||||||
@@ -437,7 +437,7 @@ export default {
|
|||||||
getBarChart(chartData, content, caller) {
|
getBarChart(chartData, content, caller) {
|
||||||
const maxX = chartData.x_axis.max;
|
const maxX = chartData.x_axis.max;
|
||||||
const minX = chartData.x_axis.min;
|
const minX = chartData.x_axis.min;
|
||||||
const getMoment = (time)=> this.$moment(time).format('YYYY/M/D hh:mm:ss');
|
const getMoment = (time)=> this.$moment(time).format('YYYY/MM/DD');
|
||||||
const getDateLabel = getDateLabelByMinMaxDate;
|
const getDateLabel = getDateLabelByMinMaxDate;
|
||||||
let datasetsPrimary = chartData.data[0].data;
|
let datasetsPrimary = chartData.data[0].data;
|
||||||
let xDataPrimary;
|
let xDataPrimary;
|
||||||
@@ -910,6 +910,349 @@ export default {
|
|||||||
|
|
||||||
return [primeVueSetData, primeVueSetOption];
|
return [primeVueSetData, primeVueSetOption];
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 建立Case By Task水平長條圖
|
||||||
|
* 避免共用 PrimeVue 設定值,避免覆蓋設定
|
||||||
|
* @param { object } chartData chart data
|
||||||
|
* @param { object } content titels 標題的文字
|
||||||
|
* @param { boolean } isSingle 單個或雙數 activity
|
||||||
|
* @param { string } xUnit x 軸單位
|
||||||
|
*/
|
||||||
|
getCaseByTaskHorizontalBarChart(chartData, content, isSingle, xUnit = 'count') {
|
||||||
|
const maxX = chartData.x_axis.max;
|
||||||
|
const getSimpleTimeLabel = simpleTimeLabel;
|
||||||
|
const getFollowTimeLabel = followTimeLabel;
|
||||||
|
const labelPrimary = chartData.data[0].label;
|
||||||
|
const labelSecondary = chartData.data[1].label;
|
||||||
|
let primeVueSetData = {};
|
||||||
|
let primeVueSetOption = {};
|
||||||
|
|
||||||
|
// 大到小排序: Primary 為主
|
||||||
|
const datasetsPrimary = chartData.data[0].data;
|
||||||
|
const datasetsSecondary = chartData.data[1].data;
|
||||||
|
datasetsPrimary.sort((a, b) => b.y - a.y);
|
||||||
|
datasetsSecondary.sort((a, b) => {
|
||||||
|
// Find the index of a.x in data1
|
||||||
|
let indexA = datasetsPrimary.findIndex(item => item.x === a.x);
|
||||||
|
// Find the index of b.x in data1
|
||||||
|
let indexB = datasetsPrimary.findIndex(item => item.x === b.x);
|
||||||
|
|
||||||
|
// Compare the indexes
|
||||||
|
return indexA - indexB;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 找出排序後,分別拉出 x, y Data
|
||||||
|
const xLabelData = datasetsPrimary.map(item => item.x);
|
||||||
|
const yDataPrimary = datasetsPrimary.map(item => item.y);
|
||||||
|
const yDataSecondary = datasetsSecondary.map(item => item.y);
|
||||||
|
|
||||||
|
primeVueSetData = {
|
||||||
|
labels: xLabelData,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: labelPrimary,
|
||||||
|
data: yDataPrimary,
|
||||||
|
backgroundColor: this.colorPrimary,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: labelSecondary,
|
||||||
|
data: yDataSecondary,
|
||||||
|
backgroundColor: this.colorSecondary,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
};
|
||||||
|
primeVueSetOption = {
|
||||||
|
indexAxis: 'y',
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
layout: {
|
||||||
|
padding: {
|
||||||
|
top: 16,
|
||||||
|
left: 8,
|
||||||
|
right: 8,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: false, // 圖例
|
||||||
|
tooltip: {
|
||||||
|
// displayColors: false,
|
||||||
|
mode: 'index', // 可顯示全部的 data label
|
||||||
|
titleFont: {weight: 'normal'},
|
||||||
|
callbacks: {}
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
color: '#334155',
|
||||||
|
font: {
|
||||||
|
size: 12,
|
||||||
|
lineHeight: 2
|
||||||
|
},
|
||||||
|
text: content.x,
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
display: true,
|
||||||
|
maxRotation: 0, // 不旋轉 lable 0~50
|
||||||
|
color: '#64748b',
|
||||||
|
callback: function(value, index, ticks) {
|
||||||
|
return value; // 計算數量,沒有時間單位才對
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
color: '#64748b',
|
||||||
|
tickLength: 0, // 網格是否超過邊線
|
||||||
|
},
|
||||||
|
border: {
|
||||||
|
display:false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
beginAtZero: true, // scale 包含 0
|
||||||
|
type: 'category',
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
color: '#334155',
|
||||||
|
font: {
|
||||||
|
size: 12,
|
||||||
|
lineHeight: 2
|
||||||
|
},
|
||||||
|
text: content.y,
|
||||||
|
},
|
||||||
|
ticks:{
|
||||||
|
color: '#64748b',
|
||||||
|
padding: 8,
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
display:false,
|
||||||
|
color: '#64748b',
|
||||||
|
},
|
||||||
|
border: {
|
||||||
|
display: false, // 隱藏左側多出來的線
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
switch (xUnit) {
|
||||||
|
case 'count':
|
||||||
|
default:
|
||||||
|
primeVueSetOption.scales.x.ticks.precision = 0; // x 軸顯示小數點後 0 位
|
||||||
|
primeVueSetOption.plugins.tooltip.callbacks.label = function(context) {
|
||||||
|
let value = context.parsed.y;
|
||||||
|
|
||||||
|
value = context.parsed.x === null ? "n/a" : context.parsed.x;
|
||||||
|
switch (context.datasetIndex) {
|
||||||
|
case 0: // Primary
|
||||||
|
return `${labelPrimary}: ${value}`;
|
||||||
|
case 1: // Secondary
|
||||||
|
return `${labelSecondary}: ${value}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(isSingle) { // 設定一個活動的 y label、提示框文字
|
||||||
|
primeVueSetOption.plugins.tooltip.callbacks.title = function(context) {
|
||||||
|
return `${content.y}: ${context[0].label}`;
|
||||||
|
};
|
||||||
|
primeVueSetOption.scales.y.ticks.callback = function (value, index, ticks) {
|
||||||
|
let label = xLabelData[index];
|
||||||
|
if(label) {
|
||||||
|
return label.length > 21 ? `${label.substring(0, 18)}...` : label
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}else { // 設定「活動」到「活動」的 y label、提示框文字
|
||||||
|
primeVueSetOption.plugins.tooltip.callbacks.title = function(context) {
|
||||||
|
return `${content.y}: ${context[0].label.replace(',', ' - ')}`
|
||||||
|
};
|
||||||
|
primeVueSetOption.scales.y.ticks.callback = function (value, index, ticks) {
|
||||||
|
let label = xLabelData[index];
|
||||||
|
let labelStart = label[0];
|
||||||
|
let labelEnd = label[1];
|
||||||
|
|
||||||
|
labelStart = labelStart.length > 10 ? `${labelStart.substring(0,7)}...` : labelStart;
|
||||||
|
labelEnd = labelEnd.length > 10 ? `${labelEnd.substring(0,7)}...` : labelEnd;
|
||||||
|
return labelStart + " - " + labelEnd
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return [primeVueSetData, primeVueSetOption]
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 建立 Average Processing Time水平長條圖
|
||||||
|
* 避免共用 PrimeVue 設定值,避免覆蓋設定
|
||||||
|
* @param { object } chartData chart data
|
||||||
|
* @param { object } content titels 標題的文字
|
||||||
|
* @param { boolean } isSingle 單個或雙數 activity
|
||||||
|
* @param { string } xUnit x 軸單位 'date' | 'count',可傳入以上任一。
|
||||||
|
*/
|
||||||
|
getAvgProcessTimeHorizontalBarChart(chartData, content, isSingle, xUnit="date") {
|
||||||
|
const maxY = chartData.y_axis.max;
|
||||||
|
const getSimpleTimeLabel = simpleTimeLabel;
|
||||||
|
const getFollowTimeLabel = followTimeLabel;
|
||||||
|
const labelPrimary = chartData.data[0].label;
|
||||||
|
const labelSecondary = chartData.data[1].label;
|
||||||
|
let primeVueSetData = {};
|
||||||
|
let primeVueSetOption = {};
|
||||||
|
|
||||||
|
// 大到小排序: Primary 為主
|
||||||
|
const datasetsPrimary = chartData.data[0].data;
|
||||||
|
const datasetsSecondary = chartData.data[1].data;
|
||||||
|
datasetsPrimary.sort((a, b) => b.y - a.y);
|
||||||
|
datasetsSecondary.sort((a, b) => {
|
||||||
|
// Find the index of a.x in data1
|
||||||
|
let indexA = datasetsPrimary.findIndex(item => item.x === a.x);
|
||||||
|
// Find the index of b.x in data1
|
||||||
|
let indexB = datasetsPrimary.findIndex(item => item.x === b.x);
|
||||||
|
|
||||||
|
// Compare the indexes
|
||||||
|
return indexA - indexB;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 找出排序後,分別拉出 x, y Data
|
||||||
|
const xLabelData = datasetsPrimary.map(item => item.x);
|
||||||
|
const yDataPrimary = datasetsPrimary.map(item => item.y);
|
||||||
|
const yDataSecondary = datasetsSecondary.map(item => item.y);
|
||||||
|
|
||||||
|
primeVueSetData = {
|
||||||
|
labels: xLabelData,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: labelPrimary,
|
||||||
|
data: yDataPrimary,
|
||||||
|
backgroundColor: this.colorPrimary,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: labelSecondary,
|
||||||
|
data: yDataSecondary,
|
||||||
|
backgroundColor: this.colorSecondary,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
};
|
||||||
|
primeVueSetOption = {
|
||||||
|
indexAxis: 'y',
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
layout: {
|
||||||
|
padding: {
|
||||||
|
top: 16,
|
||||||
|
left: 8,
|
||||||
|
right: 8,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: false, // 圖例
|
||||||
|
tooltip: {
|
||||||
|
// displayColors: false,
|
||||||
|
mode: 'index', // 可顯示全部的 data label
|
||||||
|
titleFont: {weight: 'normal'},
|
||||||
|
callbacks: {}
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
color: '#334155',
|
||||||
|
font: {
|
||||||
|
size: 12,
|
||||||
|
lineHeight: 2
|
||||||
|
},
|
||||||
|
text: content.x,
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
display: true,
|
||||||
|
maxRotation: 0, // 不旋轉 lable 0~50
|
||||||
|
color: '#64748b',
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
color: '#64748b',
|
||||||
|
tickLength: 0, // 網格是否超過邊線
|
||||||
|
},
|
||||||
|
border: {
|
||||||
|
display:false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
beginAtZero: true, // scale 包含 0
|
||||||
|
type: 'category',
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
color: '#334155',
|
||||||
|
font: {
|
||||||
|
size: 12,
|
||||||
|
lineHeight: 2
|
||||||
|
},
|
||||||
|
text: content.y
|
||||||
|
},
|
||||||
|
ticks:{
|
||||||
|
color: '#64748b',
|
||||||
|
padding: 8,
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
display:false,
|
||||||
|
color: '#64748b',
|
||||||
|
},
|
||||||
|
border: {
|
||||||
|
display: false, // 隱藏左側多出來的線
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
switch (xUnit) {
|
||||||
|
case 'date':
|
||||||
|
default:
|
||||||
|
primeVueSetOption.plugins.tooltip.callbacks.label = function(context) {
|
||||||
|
let value = context.parsed.y;
|
||||||
|
|
||||||
|
value = context.parsed.x === null ? "n/a" : getSimpleTimeLabel(context.parsed.x, 2);
|
||||||
|
switch (context.datasetIndex) {
|
||||||
|
case 0: // Primary
|
||||||
|
return `${labelPrimary}: ${value}`;
|
||||||
|
case 1: // Secondary
|
||||||
|
return `${labelSecondary}: ${value}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
primeVueSetOption.scales.x.ticks.callback = function (value, index, ticks) {
|
||||||
|
return getFollowTimeLabel(value, maxY, 1)
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(isSingle) { // 設定一個活動的 y label、提示框文字
|
||||||
|
primeVueSetOption.plugins.tooltip.callbacks.title = function(context) {
|
||||||
|
return `${content.y}: ${context[0].label}`;
|
||||||
|
};
|
||||||
|
primeVueSetOption.scales.y.ticks.callback = function (value, index, ticks) {
|
||||||
|
let label = xLabelData[index];
|
||||||
|
if(label) {
|
||||||
|
return label.length > 21 ? `${label.substring(0, 18)}...` : label
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}else { // 設定「活動」到「活動」的 y label、提示框文字
|
||||||
|
primeVueSetOption.plugins.tooltip.callbacks.title = function(context) {
|
||||||
|
return `${content.y}: ${context[0].label.replace(',', ' - ')}`
|
||||||
|
};
|
||||||
|
primeVueSetOption.scales.y.ticks.callback = function (value, index, ticks) {
|
||||||
|
let label = xLabelData[index];
|
||||||
|
let labelStart = label[0];
|
||||||
|
let labelEnd = label[1];
|
||||||
|
|
||||||
|
labelStart = labelStart.length > 10 ? `${labelStart.substring(0,7)}...` : labelStart;
|
||||||
|
labelEnd = labelEnd.length > 10 ? `${labelEnd.substring(0,7)}...` : labelEnd;
|
||||||
|
return labelStart + " - " + labelEnd
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return [primeVueSetData, primeVueSetOption]
|
||||||
|
},
|
||||||
},
|
},
|
||||||
async created() {
|
async created() {
|
||||||
this.isLoading = true; // moubeted 才停止 loading
|
this.isLoading = true; // moubeted 才停止 loading
|
||||||
@@ -944,7 +1287,7 @@ export default {
|
|||||||
this.compareDashboardData.time.avg_cycle_efficiency, this.contentData.avgCycleEfficiency, "Cycle Eff");
|
this.compareDashboardData.time.avg_cycle_efficiency, this.contentData.avgCycleEfficiency, "Cycle Eff");
|
||||||
[this.avgProcessTimeData, this.avgProcessTimeOptions] = this.getLineChart(
|
[this.avgProcessTimeData, this.avgProcessTimeOptions] = this.getLineChart(
|
||||||
this.compareDashboardData.time.avg_process_time, this.contentData.avgProcessTime, 'date');
|
this.compareDashboardData.time.avg_process_time, this.contentData.avgProcessTime, 'date');
|
||||||
[this.avgProcessTimeByTaskData, this.avgProcessTimeByTaskOptions] = this.getHorizontalBarChart(
|
[this.avgProcessTimeByTaskData, this.avgProcessTimeByTaskOptions] = this.getAvgProcessTimeHorizontalBarChart(
|
||||||
this.compareDashboardData.time.avg_process_time_by_task,
|
this.compareDashboardData.time.avg_process_time_by_task,
|
||||||
this.contentData.avgProcessTimeByTask, true, 'date');
|
this.contentData.avgProcessTimeByTask, true, 'date');
|
||||||
[this.avgWaitingTimeData, this.avgWaitingTimeOptions] = this.getLineChart(
|
[this.avgWaitingTimeData, this.avgWaitingTimeOptions] = this.getLineChart(
|
||||||
@@ -958,7 +1301,7 @@ export default {
|
|||||||
}
|
}
|
||||||
[this.freqData, this.freqOptions] = this.getLineChart(
|
[this.freqData, this.freqOptions] = this.getLineChart(
|
||||||
this.compareDashboardData.freq.cases, this.contentData.freq, 'count');
|
this.compareDashboardData.freq.cases, this.contentData.freq, 'count');
|
||||||
[this.casesByTaskData, this.casesByTaskOptions] = this.getHorizontalBarChart(
|
[this.casesByTaskData, this.casesByTaskOptions] = this.getCaseByTaskHorizontalBarChart(
|
||||||
this.compareDashboardData.freq.cases_by_task, this.contentData.casesByTask, true, 'count');
|
this.compareDashboardData.freq.cases_by_task, this.contentData.casesByTask, true, 'count');
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|||||||
Reference in New Issue
Block a user