Conformance: Time Trend chart done.

This commit is contained in:
chiayin
2023-09-04 10:22:06 +08:00
parent 5f16c4ac58
commit 3f0d1b9e05
2 changed files with 190 additions and 100 deletions

View File

@@ -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>

View File

@@ -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(); // 去除最后一个空格
}