diff --git a/src/components/Discover/Conformance/ConformanceResults.vue b/src/components/Discover/Conformance/ConformanceResults.vue index 9a3783e..ac4ba9b 100644 --- a/src/components/Discover/Conformance/ConformanceResults.vue +++ b/src/components/Discover/Conformance/ConformanceResults.vue @@ -132,7 +132,7 @@

Non-conformance Issues

-
+

Time Trendinfo @@ -176,7 +176,7 @@ import ConformanceStore from '@/stores/conformance.js'; import iconNA from '@/components/icons/IconNA.vue'; import MoreModal from './MoreModal.vue'; 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 getMoment from 'moment'; @@ -374,20 +374,20 @@ export default { }, loops: isNullLoops(data.loops), issues: isNullIsssue(data.issues), - // timeTrend: { - // not_conforming: getNumberLabel(data.counts.not_conforming), - // 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), - // xMax: data.charts.time.x_axis.max, - // xMin: data.charts.time.x_axis.min, - // yMax: data.charts.time.y_axis.max, - // yMin: data.charts.time.y_axis.min, - // } + timeTrend: { + not_conforming: getNumberLabel(data.counts.not_conforming), + 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), + xMax: data.charts.time.x_axis.max, + xMin: data.charts.time.x_axis.min, + yMax: data.charts.time.y_axis.max, + yMin: data.charts.time.y_axis.min, + } }; 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.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; }, /** @@ -571,105 +571,91 @@ export default { * time chart */ setTimeChartData(data, xMax, xMin, yMax, yMin) { - console.log(data); - // let max = yMax * 1.1; - let max = 29 * 1.1; + let max = yMax * 1.1; + // let max = 29 * 1.1; - data = [ - { - x: 434, - y: 16 - }, - { - x: 13365, - y: 19 - }, - { - x: 39228, - y: 22 - }, - { - x: 65091, - y: 18 - }, - { - x: 90954, - y: 14 - }, - { - x: 116817, - y: 21 - }, - { - x: 142681, - y: 18 - }, - { - x: 168544, - y: 16 - }, - { - x: 194407, - y: 20 - }, - { - x: 220270, - y: 10 - }, - { - x: 246133, - y: 29 - }, - { - x: 259065, - y: 29 - } -] - const start = this.selectDurationTime[0] - 1; - const end = this.selectDurationTime[1] - 1; +// data = [ +// { +// x: 434, +// y: 16 +// }, +// { +// x: 13365, +// y: 19 +// }, +// { +// x: 39228, +// y: 22 +// }, +// { +// x: 65091, +// y: 18 +// }, +// { +// x: 90954, +// y: 14 +// }, +// { +// x: 116817, +// y: 21 +// }, +// { +// x: 142681, +// y: 18 +// }, +// { +// x: 168544, +// y: 16 +// }, +// { +// x: 194407, +// y: 20 +// }, +// { +// x: 220270, +// y: 10 +// }, +// { +// x: 246133, +// y: 29 +// }, +// { +// x: 259065, +// y: 29 +// } +// ] + yMax = 29; + 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 outside = (ctx, value) => ctx.p0DataIndex < start || ctx.p1DataIndex > end ? value : undefined; - // console.log(xMax); - // console.log(xMin); this.timeChartData = { - // labels: [], - // labels: [434, 13365, 39228, 65091, 90954, 116817, 142681, 168544, 194407, 220270, 246133, 259065], + labels: formattedXVal, datasets: [ { label: 'Conforming', - data: data, - // data: [16,19,22,18,14,21,18,16,20,10,29,29], + data: yVal, fill: true, - // showLine: false, + showLine: false, tension: 0.4, backgroundColor: 'rgba(0,153,255)', pointRadius: 0, pointHitRadius: 0, spanGaps: true, segment: { - // backgroundColor: ctx => inside(ctx, 'rgb(0,153,255)') || outside(ctx, 'rgb(203, 213, 225)'), - // backgroundColor: ctx => inside(ctx, 'rgb(245,40,145)') || outside(ctx, 'rgb(250, 196, 224)'), - backgroundColor: ctx => console.log(ctx), + backgroundColor: ctx => inside(ctx, 'rgb(0,153,255)') || outside(ctx, 'rgb(255,170,68)'), }, - // 'cfm-primary': '#0099FF', - // 'cfm-secondary': '#FFAA44', x: 'x', 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, // 圖例 tooltip: false, }, - // animation: { - // onComplete: e => { - // this.resizeMask(e.chart); - // } - // }, scales: { x: { - type: 'time', ticks: { // autoSkip: false, + align: 'center', maxRotation: 0, // 不旋轉 lable 0~50 color: '#334155', display: true, @@ -738,7 +719,7 @@ export default { this.$emitter.on('coverPlate', 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.setTimeChartData(); }, @@ -752,5 +733,4 @@ export default { 'GRAD' 0, 'opsz' 20 } - diff --git a/src/module/setChartData.js b/src/module/setChartData.js index ff81691..e345519 100644 --- a/src/module/setChartData.js +++ b/src/module/setChartData.js @@ -69,3 +69,113 @@ export function setBarChartData(baseData) { // console.log(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(); // 去除最后一个空格 +}