Map Attributes: value type Slider done.

This commit is contained in:
chiayin
2023-10-25 11:13:37 +08:00
parent 937c1459d7
commit 7700d60546
3 changed files with 216 additions and 22 deletions

View File

@@ -1,18 +1,19 @@
<template> <template>
<section class="h-full"> <section class="h-full">
<p class="h2 ml-1 mb-2">Activity Select</p> <p class="h2 ml-1 mb-2">Activity Select</p>
<!-- selectedAttName: {{ selectedAttName }} <br> <!-- selectedAttName: {{ selectedAttName }} <br> -->
selectedAttRange: {{ selectedAttRange }} <br> <!-- selectedAttRange: {{ selectedAttRange }} <br> -->
attRangeData: {{ attRangeData }} --> <!-- attRangeData: {{ attRangeData }} <br> -->
<!-- filterAttrs: {{ filterAttrs }} <br> -->
<div class="flex flex-row justify-between items-start gap-4 w-full h-[calc(100%_-_48px)]"> <div class="flex flex-row justify-between items-start gap-4 w-full h-[calc(100%_-_48px)]">
<!-- Attribute Name --> <!-- Attribute Name -->
<div class="basis-1/3 bg-neutral-10 border border-neutral-300 rounded-xl px-4 pb-4 w-full h-full relative text-sm"> <div class="basis-1/3 bg-neutral-10 border border-neutral-300 rounded-xl px-4 pb-4 w-full h-full relative text-sm">
<p class="h2 border-b border-500 my-2">Attribute Name ({{ attTotal }})</p> <p class="h2 my-2">Attribute Name ({{ attTotal }})</p>
<div class="overflow-y-auto overflow-x-auto scrollbar -mx-2 h-[calc(100%_-_56px)]"> <div class="overflow-y-auto overflow-x-auto scrollbar -mx-2 h-[calc(100%_-_56px)]">
<DataTable v-model:selection="selectedAttName" :value="filterAttrs" dataKey="key" breakpoint="0" :tableClass="tableClass"> <DataTable v-model:selection="selectedAttName" :value="filterAttrs" dataKey="key" breakpoint="0" :tableClass="tableClass" @row-select="onRowSelect">
<Column selectionMode="single" :headerClass="headerModeClass" :bodyClass="bodyModeClass"></Column> <Column selectionMode="single" :headerClass="headerModeClass" :bodyClass="bodyModeClass"></Column>
<Column field="key" header="Activity" :headerClass="headerClass" :bodyClass="bodyClass" sortable></Column> <Column field="key" header="Attribute" :headerClass="headerClass" :bodyClass="bodyClass" sortable></Column>
</DataTable> </DataTable>
</div> </div>
</div> </div>
@@ -26,7 +27,7 @@
<div v-if="selectedAttName?.type === 'boolean'"> <div v-if="selectedAttName?.type === 'boolean'">
<DataTable v-model:selection="selectedAttRange" :value="attRangeData" dataKey="id" breakpoint="0" :tableClass="tableClass" @row-select="onRowSelect"> <DataTable v-model:selection="selectedAttRange" :value="attRangeData" dataKey="id" breakpoint="0" :tableClass="tableClass" @row-select="onRowSelect">
<Column selectionMode="single" :headerClass="headerModeClass" :bodyClass="bodyModeClass"></Column> <Column selectionMode="single" :headerClass="headerModeClass" :bodyClass="bodyModeClass"></Column>
<Column field="label" header="Activity" :headerClass="headerClass" :bodyClass="bodyClass"></Column> <Column field="label" header="Value" :headerClass="headerClass" :bodyClass="bodyClass"></Column>
</DataTable> </DataTable>
</div> </div>
<!-- type: string --> <!-- type: string -->
@@ -34,7 +35,7 @@
<DataTable v-model:selection="selectedAttRange" :value="attRangeData" dataKey="id" breakpoint="0" tableClass="w-full !border-separate !border-spacing-x-2 !table-auto text-sm" @row-select="onRowSelect"> <DataTable v-model:selection="selectedAttRange" :value="attRangeData" dataKey="id" breakpoint="0" tableClass="w-full !border-separate !border-spacing-x-2 !table-auto text-sm" @row-select="onRowSelect">
<ColumnGroup type="header"> <ColumnGroup type="header">
<Row> <Row>
<Column selectionMode="multiple" :headerClass="headerModeClass"></Column> <Column selectionMode="multiple" :headerClass="headerModeClass" ></Column>
<Column field="value" header="Value" :headerClass="headerClass" sortable /> <Column field="value" header="Value" :headerClass="headerClass" sortable />
<Column field="freq" header="Occurrences" :headerClass="headerClass" sortable :colspan="3" /> <Column field="freq" header="Occurrences" :headerClass="headerClass" sortable :colspan="3" />
</Row> </Row>
@@ -52,8 +53,29 @@
<Column field="occ_ratio" header="Occurrence Ratio" bodyClass="!text-right !py-2 !border-0"></Column> <Column field="occ_ratio" header="Occurrence Ratio" bodyClass="!text-right !py-2 !border-0"></Column>
</DataTable> </DataTable>
</div> </div>
<!-- type: value -->
<div v-else> <div v-else>
other <div class="h-3/5 relative">
<!-- <canvas id="chartCanvasId"></canvas> -->
<Chart type="line" :data="chartData" :options="chartOptions" class="h-30rem" id="chartCanvasId"/>
<div id="chart-mask-left" class="absolute bg-neutral-10/50"></div>
<div id="chart-mask-right" class="absolute bg-neutral-10/50"></div>
</div>
<div class="px-2 py-3">
<Slider v-model="selectArea" :step="1" :min="0" :max="selectRange" range class="mx-2" @change="changeSelectArea($event)"/>
</div>
<!-- Calendar group -->
<!-- <div class="flex justify-center items-center space-x-2 w-full">
<div>
<span class="block mb-2">Start time</span>
<Calendar v-model="startTime" dateFormat="yy/mm/dd" :panelProps="panelProps" :minDate="startMinDate" :maxDate="startMaxDate" showTime showIcon hourFormat="24" @date-select="sliderTimeRange($event, 'start')" id="startCalendar"/>
</div>
<span class="block mt-4">~</span>
<div>
<span class="block mb-2">End time</span>
<Calendar v-model="endTime" dateFormat="yy/mm/dd" :panelProps="panelProps" :minDate="endMinDate" :maxDate="endMaxDate" showTime showIcon hourFormat="24" @date-select="sliderTimeRange($event, 'end')" id="endCalendar"/>
</div>
</div> -->
</div> </div>
</div> </div>
</div> </div>
@@ -63,6 +85,10 @@
<script> <script>
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import AllMapDataStore from '@/stores/allMapData.js'; import AllMapDataStore from '@/stores/allMapData.js';
import { Chart, registerables } from 'chart.js';
import 'chartjs-adapter-date-fns';
import { setLineChartData, setBarChartData, timeRange, yTimeRange, getXIndex, formatTime } from '@/module/setChartData.js';
import getMoment from 'moment';
export default { export default {
setup() { setup() {
@@ -75,13 +101,24 @@ export default {
return { return {
selectedAttName: null, selectedAttName: null,
selectedAttRange: null, selectedAttRange: null,
valueTypes: ['int', 'float', 'date'],
classTypes: ['boolean', 'string'],
chartData: null,
chartOptions: null,
chartComplete: null, // 已出圖的 chart.js 資料
selectArea: null,
selectRange: 1000, // 更改 select 的切分數
startTime: null,
endTime: null,
startMinDate: null,
startMaxDate: null,
endMinDate: null,
endMaxDate: null,
tableClass: 'w-full h-full !border-separate !border-spacing-x-2 !table-auto text-sm', tableClass: 'w-full h-full !border-separate !border-spacing-x-2 !table-auto text-sm',
headerModeClass: 'w-8 !p-2 !bg-neutral-10 !border-neutral-500 sticky top-0 left-0 z-10 bg-neutral-10', headerModeClass: 'w-8 !p-2 !bg-neutral-10 !border-neutral-500 sticky top-0 left-0 z-10 bg-neutral-10',
headerClass: '!bg-neutral-10 !border-neutral-500 !py-2 sticky top-0 left-0 z-10 bg-neutral-10', headerClass: '!bg-neutral-10 !border-neutral-500 !py-2 sticky top-0 left-0 z-10 bg-neutral-10',
bodyModeClass: '!p-2 !border-0', bodyModeClass: '!p-2 !border-0',
bodyClass: 'break-words !py-2 !border-0', bodyClass: 'break-words !py-2 !border-0',
valueTypes: ['int', 'float', 'date'],
classTypes: ['boolean', 'string'],
} }
}, },
computed: { computed: {
@@ -139,19 +176,34 @@ export default {
default: default:
return null; return null;
} }
},
// if(valueTypes.includes(type)){ // 取出選取的 Attribute radio 數值型的資料
// data = { valueData: function() {
// filter 回傳陣列find 回傳遞一個找到的元素,因此使用 find 方法。
// } if(this.valueTypes.includes(this.selectedAttName?.type)){
// }else if(classTypes.includes(type)){ const valueData = this.filterAttrs.find(item => item.type === this.selectedAttName?.type && item.key === this.selectedAttName?.key);
return valueData
// }
} }
}, },
// 找出 slidrData時間格式:毫秒時間戳
sliderData: function() {
let xAxisMin = new Date(this.valueData.chart.x_axis.min).getTime();
let xAxisMax = new Date(this.valueData.chart.x_axis.max).getTime();
let range = xAxisMax - xAxisMin;
let step = range / this.selectRange;
let sliderData = []
for (let i = 0; i <= this.selectRange; i++) {
sliderData.push(xAxisMin + (step * i));
}
return sliderData;
},
},
methods: { methods: {
onRowSelect(e) { onRowSelect(e) {
// this.$emit('on-row-select', e) if(this.valueData) this.createChart();
// this.$emit('on-row-select', e.data)
}, },
/** /**
* set progress bar width * set progress bar width
@@ -170,6 +222,149 @@ export default {
if((val * 100).toFixed(1) >= 100) return `100%`; if((val * 100).toFixed(1) >= 100) return `100%`;
else return `${(val * 100).toFixed(1)}%`; else return `${(val * 100).toFixed(1)}%`;
}, },
/**
* 調整遮罩大小
* @param {object} chart 取得 chart.js 資料
*/
resizeMask(chart) {
let from = (this.selectArea[0] * 0.01) / (this.selectRange * 0.01);
let to = (this.selectArea[1] * 0.01) / (this.selectRange * 0.01);
this.resizeLeftMask(chart, from);
this.resizeRightMask(chart, to);
}, },
/**
* 調整左邊遮罩大小
* @param {object} chart 取得 chart.js 資料
* @param {number} chart 取得 chart.js 資料
*/
resizeLeftMask(chart, from) {
const canvas = document.querySelector('#chartCanvasId canvas');
const mask = document.getElementById("chart-mask-left");
mask.style.left = `${canvas.offsetLeft + chart.chartArea.left}px`;
mask.style.width = `${chart.chartArea.width * from}px`;
mask.style.top = `${canvas.offsetTop + chart.chartArea.top}px`;
mask.style.height = `${chart.chartArea.height}px`;
},
/**
* 調整又邊遮罩大小
* @param {object} chart 取得 chart.js 資料
*/
resizeRightMask(chart, to) {
const canvas = document.querySelector('#chartCanvasId canvas');
const mask = document.getElementById("chart-mask-right");
mask.style.left = `${canvas.offsetLeft + chart.chartArea.left + chart.chartArea.width * to}px`;
mask.style.width = `${chart.chartArea.width * (1 - to)}px`;
mask.style.top = `${canvas.offsetTop + chart.chartArea.top}px`;
mask.style.height = `${chart.chartArea.height}px`;
},
/**
* create chart
* @param {object} valueData 選取的 Attribute包含後端原始數據的 type、key
*/
createChart() {
const valueData = this.valueData;
const max = valueData.chart.y_axis.max * 1.1;
const data = setLineChartData(valueData.chart.data, valueData.chart.x_axis.max, valueData.chart.x_axis.min);
this.chartData = {
datasets: [
{
label: 'Case',
data: data,
fill: 'start',
showLine: false,
tension: 0.4,
backgroundColor: 'rgba(0,153,255)',
pointRadius: 0,
x: 'x',
y: 'y',
}
]
};
this.chartOptions = {
responsive: true,
maintainAspectRatio: false,
layout: {
padding: {
top: 16,
left: 8,
right: 8,
}
},
plugins: {
legend: false, // 圖例
filler: {
propagate: false
},
title: false
},
// animations: false, // 取消動畫
animation: {
onComplete: e => {
this.chartComplete = e.chart;
this.resizeMask(e.chart);
}
},
interaction: {
intersect: true,
},
scales: {
x: {
type: 'time',
ticks: {
autoSkip: false,
maxRotation: 0, // 不旋轉 lable 0~50
color: '#334155',
display: true,
},
grid: {
display: false, // 隱藏 x 軸網格
},
},
y: {
beginAtZero: true, // scale 包含 0
max: max,
ticks: { // 設定間隔數值
display: false, // 隱藏數值,只顯示格線
stepSize: max / 4,
},
grid: {
color: 'rgba(100,116,139)',
z: 1,
},
border: {
display: false, // 隱藏左側多出來的線
}
},
},
};
},
/**
* 滑塊改變的時候
* @param {array} e [1, 100]
*/
changeSelectArea(e) {
// 日曆改變時,滑塊跟著改變
let sliderData = this.sliderData;
let start = sliderData[e[0].toFixed()];
let end = sliderData[e[1].toFixed()]; // 取得 index須為整數。
this.startTime = new Date(start);
this.endTime = new Date(end);
// 重新設定 start end 日曆選取範圍
this.endMinDate = new Date(start);
this.startMaxDate = new Date(end);
// 重新算圖
this.resizeMask(this.chartComplete);
// 執行 timeFrameStartEnd 才會改變數據
// this.timeFrameStartEnd;
},
},
mounted() {
// this.setChartData();
this.selectArea = [0, this.selectRange]; // 初始化滑塊
}
} }
</script> </script>

View File

@@ -315,5 +315,3 @@ export default{
} }
} }
</script> </script>
<style>
</style>

View File

@@ -5,6 +5,7 @@ import getMoment from 'moment';
* @param {array} baseData 後端給的 10 個時間點 * @param {array} baseData 後端給的 10 個時間點
* @param {number} xMax 2022-05-23T18:00:00 * @param {number} xMax 2022-05-23T18:00:00
* @param {number} xMin 2022-05-23T08:00:00 * @param {number} xMin 2022-05-23T08:00:00
* @param {number} isPercent 是否以百分比顯示
* @param {number} yMax case * @param {number} yMax case
* @param {number} yMin case * @param {number} yMin case
* @returns {array} 可直接套入 chart.js 的 data * @returns {array} 可直接套入 chart.js 的 data