Conformance: Time Trend chart done.
This commit is contained in:
@@ -132,7 +132,7 @@
|
|||||||
<p class="h2 text-base">Non-conformance Issues</p>
|
<p class="h2 text-base">Non-conformance Issues</p>
|
||||||
<div class="flex gap-4 w-full">
|
<div class="flex gap-4 w-full">
|
||||||
<!-- Issues chart -->
|
<!-- Issues chart -->
|
||||||
<div v-if="null" class="border rounded border-neutral-300 p-2 bg-neutral-10 w-full" >
|
<div class="border rounded border-neutral-300 p-2 bg-neutral-10 w-full" >
|
||||||
<!-- v-if="data.timeTrend.chart == null" -->
|
<!-- v-if="data.timeTrend.chart == null" -->
|
||||||
<p class="h2 pl-2 flex justify-between items-center">
|
<p class="h2 pl-2 flex justify-between items-center">
|
||||||
<span>Time Trend<span class="material-symbols-outlined text-sm align-middle ml-2">info</span></span>
|
<span>Time Trend<span class="material-symbols-outlined text-sm align-middle ml-2">info</span></span>
|
||||||
@@ -176,7 +176,7 @@ import ConformanceStore from '@/stores/conformance.js';
|
|||||||
import iconNA from '@/components/icons/IconNA.vue';
|
import iconNA from '@/components/icons/IconNA.vue';
|
||||||
import MoreModal from './MoreModal.vue';
|
import MoreModal from './MoreModal.vue';
|
||||||
import getNumberLabel from '@/module/numberLabel.js';
|
import getNumberLabel from '@/module/numberLabel.js';
|
||||||
import { setLineChartData, setBarChartData } from '@/module/setChartData.js';
|
import { setLineChartData, setBarChartData, timeRange, yTimeRange, getXIndex, formatTime } from '@/module/setChartData.js';
|
||||||
import abbreviateNumber from '@/module/abbreviateNumber.js';
|
import abbreviateNumber from '@/module/abbreviateNumber.js';
|
||||||
import getMoment from 'moment';
|
import getMoment from 'moment';
|
||||||
|
|
||||||
@@ -374,20 +374,20 @@ export default {
|
|||||||
},
|
},
|
||||||
loops: isNullLoops(data.loops),
|
loops: isNullLoops(data.loops),
|
||||||
issues: isNullIsssue(data.issues),
|
issues: isNullIsssue(data.issues),
|
||||||
// timeTrend: {
|
timeTrend: {
|
||||||
// not_conforming: getNumberLabel(data.counts.not_conforming),
|
not_conforming: getNumberLabel(data.counts.not_conforming),
|
||||||
// total: getNumberLabel(sum),
|
total: getNumberLabel(sum),
|
||||||
// chart: setLineChartData(data.charts.time.data, data.charts.time.x_axis.max, data.charts.time.x_axis.min, false, data.charts.time.y_axis.max, data.charts.time.y_axis.min),
|
chart: setLineChartData(data.charts.time.data, data.charts.time.x_axis.max, data.charts.time.x_axis.min, false, data.charts.time.y_axis.max, data.charts.time.y_axis.min),
|
||||||
// xMax: data.charts.time.x_axis.max,
|
xMax: data.charts.time.x_axis.max,
|
||||||
// xMin: data.charts.time.x_axis.min,
|
xMin: data.charts.time.x_axis.min,
|
||||||
// yMax: data.charts.time.y_axis.max,
|
yMax: data.charts.time.y_axis.max,
|
||||||
// yMin: data.charts.time.y_axis.min,
|
yMin: data.charts.time.y_axis.min,
|
||||||
// }
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setRateChartData(result.charts.rate.data); // 建立圖表 Rate Chart.js
|
this.setRateChartData(result.charts.rate.data); // 建立圖表 Rate Chart.js
|
||||||
this.setCasesChartData(result.charts.cases.data.conforming, result.charts.cases.data.not_conforming, data.charts.cases.x_axis.max, data.charts.cases.x_axis.min); // 建立圖表 Cases Chart.js
|
this.setCasesChartData(result.charts.cases.data.conforming, result.charts.cases.data.not_conforming, data.charts.cases.x_axis.max, data.charts.cases.x_axis.min); // 建立圖表 Cases Chart.js
|
||||||
// this.setTimeChartData(result.timeTrend.chart, result.timeTrend.xMax, result.timeTrend.xMin, result.timeTrend.yMax); // 建立圖表 Time Chart.js
|
this.setTimeChartData(result.timeTrend.chart, result.timeTrend.xMax, result.timeTrend.xMin, result.timeTrend.yMax, result.timeTrend.yMax, result.timeTrend.yMin); // 建立圖表 Time Chart.js
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -571,105 +571,91 @@ export default {
|
|||||||
* time chart
|
* time chart
|
||||||
*/
|
*/
|
||||||
setTimeChartData(data, xMax, xMin, yMax, yMin) {
|
setTimeChartData(data, xMax, xMin, yMax, yMin) {
|
||||||
console.log(data);
|
let max = yMax * 1.1;
|
||||||
// let max = yMax * 1.1;
|
// let max = 29 * 1.1;
|
||||||
let max = 29 * 1.1;
|
|
||||||
|
|
||||||
data = [
|
// data = [
|
||||||
{
|
// {
|
||||||
x: 434,
|
// x: 434,
|
||||||
y: 16
|
// y: 16
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
x: 13365,
|
// x: 13365,
|
||||||
y: 19
|
// y: 19
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
x: 39228,
|
// x: 39228,
|
||||||
y: 22
|
// y: 22
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
x: 65091,
|
// x: 65091,
|
||||||
y: 18
|
// y: 18
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
x: 90954,
|
// x: 90954,
|
||||||
y: 14
|
// y: 14
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
x: 116817,
|
// x: 116817,
|
||||||
y: 21
|
// y: 21
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
x: 142681,
|
// x: 142681,
|
||||||
y: 18
|
// y: 18
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
x: 168544,
|
// x: 168544,
|
||||||
y: 16
|
// y: 16
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
x: 194407,
|
// x: 194407,
|
||||||
y: 20
|
// y: 20
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
x: 220270,
|
// x: 220270,
|
||||||
y: 10
|
// y: 10
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
x: 246133,
|
// x: 246133,
|
||||||
y: 29
|
// y: 29
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
x: 259065,
|
// x: 259065,
|
||||||
y: 29
|
// y: 29
|
||||||
}
|
// }
|
||||||
]
|
// ]
|
||||||
const start = this.selectDurationTime[0] - 1;
|
yMax = 29;
|
||||||
const end = this.selectDurationTime[1] - 1;
|
let xVal = timeRange(434, 259065, 100);
|
||||||
|
let yVal = yTimeRange(data, 100, yMin, yMax);
|
||||||
|
// console.log(xVal);
|
||||||
|
data = xVal.map((x, index) => ({ x, y: yVal[index] }));
|
||||||
|
let formattedXVal = xVal.map(value => formatTime(value));
|
||||||
|
let selectTimeMinIndex = getXIndex(xVal, this.selectDurationTime.min);
|
||||||
|
let selectTimeMaxIndex = getXIndex(xVal, this.selectDurationTime.max);
|
||||||
|
const start = selectTimeMinIndex;
|
||||||
|
const end = selectTimeMaxIndex;
|
||||||
const inside = (ctx, value) => ctx.p0DataIndex >= start && ctx.p1DataIndex <= end ? value : undefined;
|
const inside = (ctx, value) => ctx.p0DataIndex >= start && ctx.p1DataIndex <= end ? value : undefined;
|
||||||
const outside = (ctx, value) => ctx.p0DataIndex < start || ctx.p1DataIndex > end ? value : undefined;
|
const outside = (ctx, value) => ctx.p0DataIndex < start || ctx.p1DataIndex > end ? value : undefined;
|
||||||
|
|
||||||
// console.log(xMax);
|
|
||||||
// console.log(xMin);
|
|
||||||
this.timeChartData = {
|
this.timeChartData = {
|
||||||
// labels: [],
|
labels: formattedXVal,
|
||||||
// labels: [434, 13365, 39228, 65091, 90954, 116817, 142681, 168544, 194407, 220270, 246133, 259065],
|
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
label: 'Conforming',
|
label: 'Conforming',
|
||||||
data: data,
|
data: yVal,
|
||||||
// data: [16,19,22,18,14,21,18,16,20,10,29,29],
|
|
||||||
fill: true,
|
fill: true,
|
||||||
// showLine: false,
|
showLine: false,
|
||||||
tension: 0.4,
|
tension: 0.4,
|
||||||
backgroundColor: 'rgba(0,153,255)',
|
backgroundColor: 'rgba(0,153,255)',
|
||||||
pointRadius: 0,
|
pointRadius: 0,
|
||||||
pointHitRadius: 0,
|
pointHitRadius: 0,
|
||||||
spanGaps: true,
|
spanGaps: true,
|
||||||
segment: {
|
segment: {
|
||||||
// backgroundColor: ctx => inside(ctx, 'rgb(0,153,255)') || outside(ctx, 'rgb(203, 213, 225)'),
|
backgroundColor: ctx => inside(ctx, 'rgb(0,153,255)') || outside(ctx, 'rgb(255,170,68)'),
|
||||||
// backgroundColor: ctx => inside(ctx, 'rgb(245,40,145)') || outside(ctx, 'rgb(250, 196, 224)'),
|
|
||||||
backgroundColor: ctx => console.log(ctx),
|
|
||||||
},
|
},
|
||||||
// 'cfm-primary': '#0099FF',
|
|
||||||
// 'cfm-secondary': '#FFAA44',
|
|
||||||
x: 'x',
|
x: 'x',
|
||||||
y: 'y',
|
y: 'y',
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// label: 'Not Conforming',
|
|
||||||
// // data: data,
|
|
||||||
// data: [16,19,22,18,14,21,18,16,20,10,29,29],
|
|
||||||
// // fill: true,
|
|
||||||
// // showLine: false,
|
|
||||||
// tension: 0.4,
|
|
||||||
// backgroundColor: 'rgba(0,153,255)',
|
|
||||||
// pointRadius: 0,
|
|
||||||
// pointHitRadius: 0,
|
|
||||||
// // x: 'x',
|
|
||||||
// // y: 'y',
|
|
||||||
// },
|
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -687,16 +673,11 @@ export default {
|
|||||||
legend: false, // 圖例
|
legend: false, // 圖例
|
||||||
tooltip: false,
|
tooltip: false,
|
||||||
},
|
},
|
||||||
// animation: {
|
|
||||||
// onComplete: e => {
|
|
||||||
// this.resizeMask(e.chart);
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
scales: {
|
scales: {
|
||||||
x: {
|
x: {
|
||||||
type: 'time',
|
|
||||||
ticks: {
|
ticks: {
|
||||||
// autoSkip: false,
|
// autoSkip: false,
|
||||||
|
align: 'center',
|
||||||
maxRotation: 0, // 不旋轉 lable 0~50
|
maxRotation: 0, // 不旋轉 lable 0~50
|
||||||
color: '#334155',
|
color: '#334155',
|
||||||
display: true,
|
display: true,
|
||||||
@@ -738,7 +719,7 @@ export default {
|
|||||||
this.$emitter.on('coverPlate', boolean => {
|
this.$emitter.on('coverPlate', boolean => {
|
||||||
this.isCoverPlate = boolean;
|
this.isCoverPlate = boolean;
|
||||||
});
|
});
|
||||||
// this.$emitter.on('timeRangeMaxMin', data => this.selectDurationTime = data);
|
this.$emitter.on('timeRangeMaxMin', data => this.selectDurationTime = data);
|
||||||
// this.selectDurationTime = { min: 86834, max: 176265};
|
// this.selectDurationTime = { min: 86834, max: 176265};
|
||||||
// this.setTimeChartData();
|
// this.setTimeChartData();
|
||||||
},
|
},
|
||||||
@@ -752,5 +733,4 @@ export default {
|
|||||||
'GRAD' 0,
|
'GRAD' 0,
|
||||||
'opsz' 20
|
'opsz' 20
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -69,3 +69,113 @@ export function setBarChartData(baseData) {
|
|||||||
// console.log(data);
|
// console.log(data);
|
||||||
return data
|
return data
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* 將一段時間均分成多段的時間點
|
||||||
|
* @param {string} startTime 開始時間(ex: 434) 總秒數
|
||||||
|
* @param {string} endTime 結束時間(ex: 259065) 總秒數
|
||||||
|
* @param {number} amount 切分成多少段
|
||||||
|
* @returns {array} 均分成多段的時間 array
|
||||||
|
*/
|
||||||
|
export function timeRange(minTime, maxTime, amount) {
|
||||||
|
// x 軸(時間軸)的範圍是最大-最小,從最小值按照 index 累加間距到最大值
|
||||||
|
const startTime = minTime;
|
||||||
|
const endTime = maxTime;
|
||||||
|
let timeRange = []; // return數據初始化
|
||||||
|
const timeGap = (endTime - startTime) / (amount - 1); // 切分成多少段
|
||||||
|
|
||||||
|
for (let i = 0; i < amount; i++) {
|
||||||
|
timeRange.push(startTime + timeGap * i);
|
||||||
|
}
|
||||||
|
timeRange = timeRange.map(value => Math.round(value));
|
||||||
|
return timeRange;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 將 y 軸的值分割成跟 x 時間軸一樣的等份,使用統計學 R 語言算出貝茲曲線攻勢
|
||||||
|
* @param {array} data 切分成多少段
|
||||||
|
* @param {number} yAmount 切分成多少段
|
||||||
|
* @param {number} yMax y 最大值
|
||||||
|
* @returns {array} 均分成多段的時間 array
|
||||||
|
*/
|
||||||
|
export function yTimeRange(data, yAmount, yMax) {
|
||||||
|
const yRange = [];
|
||||||
|
const yGap = (1/ (yAmount-1));
|
||||||
|
|
||||||
|
// 貝茲曲線公式
|
||||||
|
const threebsr = function (t, a1, a2, a3, a4) {
|
||||||
|
return (
|
||||||
|
(1 - t) * (1 - t) * (1 - t) * a1 +
|
||||||
|
3 * t * (1 - t)* (1 - t) * a2 +
|
||||||
|
3 * t * t * (1 - t) * a3 +
|
||||||
|
t * t * t * a4
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let j = 0; j < data.length - 1; j++) {
|
||||||
|
for (let i = 0; i <= 1; i += yGap*11) {
|
||||||
|
yRange.push(threebsr(i, data[j].y, data[j].y, data[j + 1].y, data[j + 1].y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(yRange.length < yAmount) {
|
||||||
|
let len = yAmount - yRange.length;
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
yRange.push(yMax)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(yRange.length > yAmount) {
|
||||||
|
let len = yRange.length - yAmount;
|
||||||
|
for(let i = 0; i < len; i++) {
|
||||||
|
yRange.splice(1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return yRange;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 找出選擇的時間點的 index
|
||||||
|
* @param {array} data xVal
|
||||||
|
* @param {number} xValue x 值
|
||||||
|
* @returns {numver} x index
|
||||||
|
*/
|
||||||
|
export function getXIndex(data, xValue) {
|
||||||
|
let closestElement = data[0]; // 假定第一个元素是最接近的
|
||||||
|
let closestIndex = xValue; // 假定第一个元素的索引是 0
|
||||||
|
let smallestDifference = Math.abs(xValue - data[0]); // 初始差值设为第一个元素与目标数的差值
|
||||||
|
|
||||||
|
for (let i = 1; i < data.length; i++) {
|
||||||
|
let difference = Math.abs(xValue - data[i]);
|
||||||
|
|
||||||
|
if (difference < smallestDifference) {
|
||||||
|
closestElement = data[i];
|
||||||
|
closestIndex = i;
|
||||||
|
smallestDifference = difference;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return closestIndex;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} seconds
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function formatTime(seconds) {
|
||||||
|
const days = Math.floor(seconds / (3600 * 24));
|
||||||
|
const hours = Math.floor((seconds % (3600 * 24)) / 3600);
|
||||||
|
const minutes = Math.floor((seconds % 3600) / 60);
|
||||||
|
const remainingSeconds = seconds % 60;
|
||||||
|
|
||||||
|
let result = '';
|
||||||
|
if (days > 0) {
|
||||||
|
result += `${days}d`;
|
||||||
|
}
|
||||||
|
if (hours > 0) {
|
||||||
|
result += `${hours}h`;
|
||||||
|
}
|
||||||
|
if (minutes > 0) {
|
||||||
|
result += `${minutes}m`;
|
||||||
|
}
|
||||||
|
result += `${remainingSeconds}s`;
|
||||||
|
|
||||||
|
return result.trim(); // 去除最后一个空格
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user