Map Attributes: value type int and float done.

This commit is contained in:
chiayin
2023-10-30 10:06:54 +08:00
parent bca9978fad
commit 03442f1934
2 changed files with 149 additions and 50 deletions

View File

@@ -24,14 +24,14 @@
</div> </div>
<div class="overflow-y-auto overflow-x-auto scrollbar -mx-2 w-full h-[calc(100%_-_70px)]"> <div class="overflow-y-auto overflow-x-auto scrollbar -mx-2 w-full h-[calc(100%_-_70px)]">
<!-- type: boolean --> <!-- type: boolean -->
<div v-if="selectedAttName?.type === 'boolean'" class="w-full"> <div v-if="selectedAttName.type === 'boolean'" class="w-full">
<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="Value" :headerClass="headerClass" :bodyClass="bodyClass"></Column> <Column field="label" header="Value" :headerClass="headerClass" :bodyClass="bodyClass"></Column>
</DataTable> </DataTable>
</div> </div>
<!-- type: string --> <!-- type: string -->
<div v-else-if="selectedAttName?.type === 'string'" class="w-full"> <div v-else-if="selectedAttName.type === 'string'" class="w-full">
<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>
@@ -54,7 +54,7 @@
</DataTable> </DataTable>
</div> </div>
<!-- type: value --> <!-- type: value -->
<div v-else-if="valueTypes.includes(selectedAttName?.type)" class="space-y-2 text-sm w-full"> <div v-else-if="valueTypes.includes(selectedAttName.type)" class="space-y-2 text-sm w-full">
<!-- Chart.js --> <!-- Chart.js -->
<div class="h-3/5 relative"> <div class="h-3/5 relative">
<Chart type="line" :data="chartData" :options="chartOptions" class="h-30rem" id="chartCanvasId"/> <Chart type="line" :data="chartData" :options="chartOptions" class="h-30rem" id="chartCanvasId"/>
@@ -65,16 +65,24 @@
<div class="px-2 py-3"> <div class="px-2 py-3">
<Slider v-model="selectArea" :step="1" :min="0" :max="selectRange" range class="mx-2" @change="changeSelectArea($event)"/> <Slider v-model="selectArea" :step="1" :min="0" :max="selectRange" range class="mx-2" @change="changeSelectArea($event)"/>
</div> </div>
<div>
<!-- Calendar group --> <!-- Calendar group -->
<div class="flex justify-center items-center space-x-2 w-full"> <div v-if="selectedAttName.type === 'date'" class="flex justify-center items-center space-x-2 w-full">
<div> <div>
<span class="block mb-2">Start time</span> <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" /> <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="sliderValueRange($event, 'end')" id="endCalendar"/>
</div>
</div> </div>
<span class="block mt-4">~</span> <!-- InputNumber group -->
<div> <div v-else class="flex justify-center items-center space-x-2 w-full w-">
<span class="block mb-2">End time</span> <InputNumber v-model="valueStart" :min="valueStartMin" :max="valueStartMax" :maxFractionDigits="2" inputClass="w-24 text-sm" @blur="sliderValueRange($event, 'start')"></InputNumber>
<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"/> <span class="block px-2">~</span>
<InputNumber v-model="valueEnd" :min="valueEndMin" :max="valueEndMax" :maxFractionDigits="2" inputClass="w-24 text-sm" @blur="sliderValueRange($event, 'end')"></InputNumber>
</div> </div>
</div> </div>
</div> </div>
@@ -86,10 +94,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 } from '@/module/setChartData.js'; import { setLineChartData } from '@/module/setChartData.js';
import getMoment from 'moment'; import getMoment from 'moment';
import InputNumber from 'primevue/inputnumber';
import { Decimal } from 'decimal.js';
export default { export default {
setup() { setup() {
@@ -100,7 +108,7 @@ export default {
}, },
data() { data() {
return { return {
selectedAttName: null, selectedAttName: {},
selectedAttRange: null, selectedAttRange: null,
valueTypes: ['int', 'float', 'date'], valueTypes: ['int', 'float', 'date'],
classTypes: ['boolean', 'string'], classTypes: ['boolean', 'string'],
@@ -109,12 +117,18 @@ export default {
chartComplete: null, // 已出圖的 chart.js 資料 chartComplete: null, // 已出圖的 chart.js 資料
selectArea: null, selectArea: null,
selectRange: 1000, // 更改 select 的切分數 selectRange: 1000, // 更改 select 的切分數
startTime: null, startTime: null, // PrimeVue Calendar v-model
endTime: null, endTime: null, // PrimeVue Calendar v-model
startMinDate: null, startMinDate: null,
startMaxDate: null, startMaxDate: null,
endMinDate: null, endMinDate: null,
endMaxDate: null, endMaxDate: null,
valueStart: null, // PrimeVue InputNumber v-model
valueEnd: null, // PrimeVue InputNumber v-model
valueStartMin: null,
valueStartMax: null,
valueEndMin: null,
valueEndMax: 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',
@@ -132,7 +146,7 @@ export default {
return this.filterAttrs.length; return this.filterAttrs.length;
}, },
attRangeTotal: function() { attRangeTotal: function() {
const type = this.selectedAttName?.type; const type = this.selectedAttName.type;
return !this.classTypes.includes(type) ? null return !this.classTypes.includes(type) ? null
: this.attRangeData ? `(${this.attRangeData.length})` : this.attRangeData ? `(${this.attRangeData.length})`
@@ -140,7 +154,7 @@ export default {
}, },
attRangeData: function() { attRangeData: function() {
let data = []; let data = [];
const type = this.selectedAttName?.type; const type = this.selectedAttName.type;
switch (type) { switch (type) {
case 'boolean': case 'boolean':
@@ -186,15 +200,26 @@ export default {
// 取出選取的 Attribute radio 數值型的資料 // 取出選取的 Attribute radio 數值型的資料
valueData: function() { valueData: function() {
// filter 回傳陣列find 回傳遞一個找到的元素,因此使用 find 方法。 // filter 回傳陣列find 回傳遞一個找到的元素,因此使用 find 方法。
if(this.valueTypes.includes(this.selectedAttName?.type)){ if(this.valueTypes.includes(this.selectedAttName.type)){
const valueData = this.filterAttrs.find(item => item.type === this.selectedAttName?.type && item.key === this.selectedAttName?.key); const valueData = this.filterAttrs.find(item => item.type === this.selectedAttName.type && item.key === this.selectedAttName.key);
return valueData return valueData
} }
}, },
// 找出 slidrData時間格式:毫秒時間戳 // 找出 slidrData時間格式:毫秒時間戳
sliderData: function() { sliderData: function() {
let xAxisMin = new Date(this.valueData.chart.x_axis.min).getTime(); let xAxisMin;
let xAxisMax = new Date(this.valueData.chart.x_axis.max).getTime(); let xAxisMax;
const type = this.valueData.type;
switch (type) {
case 'date':
xAxisMin = new Date(this.valueData.chart.x_axis.min).getTime();
xAxisMax = new Date(this.valueData.chart.x_axis.max).getTime();
break;
default:
xAxisMin = Math.round(this.valueData.chart.x_axis.min);
xAxisMax = Math.round(this.valueData.chart.x_axis.max);
break;
}
let range = xAxisMax - xAxisMin; let range = xAxisMax - xAxisMin;
let step = range / this.selectRange; let step = range / this.selectRange;
let sliderData = [] let sliderData = []
@@ -202,13 +227,32 @@ export default {
for (let i = 0; i <= this.selectRange; i++) { for (let i = 0; i <= this.selectRange; i++) {
sliderData.push(xAxisMin + (step * i)); sliderData.push(xAxisMin + (step * i));
} }
switch (type) {
case 'int':
sliderData = sliderData.map(value => Math.round(value));
break;
case 'float':
sliderData = sliderData.map(value => new Decimal(value.toFixed(2)));
break;
default:
break;
}
return sliderData; return sliderData;
}, },
// user select time start and end // user select time start and end
attValueTypeStartEnd: function() { attValueTypeStartEnd: function() {
let start = getMoment(this.startTime).format('YYYY-MM-DDTHH:mm:ss'); let start;
let end = getMoment(this.endTime).format('YYYY-MM-DDTHH:mm:ss'); let end;
switch (this.selectedAttName.type) {
case 'date':
start = getMoment(this.startTime).format('YYYY-MM-DDTHH:mm:ss');
end = getMoment(this.endTime).format('YYYY-MM-DDTHH:mm:ss');
break;
default:
start = this.valueStart;
end = this.valueEnd;
break;
}
// this.selectTimeFrame = [start, end]; // 傳給後端的資料 // this.selectTimeFrame = [start, end]; // 傳給後端的資料
// this.$emit('on-row-select', e.data); // this.$emit('on-row-select', e.data);
@@ -216,20 +260,49 @@ export default {
}, },
}, },
methods: { methods: {
/**
* 切換 Attribute Name 或選取類別型 table 的選項
* @param {event} e
*/
onRowSelect(e) { onRowSelect(e) {
if(this.valueData) { if(this.valueData) { // 切換 Attribute Name
// 初始化: Calendar // 初始化雙向綁定
this.startMinDate = new Date(getMoment(this.valueData.chart.x_axis.min).format()); this.selectArea = [0, this.selectRange];
this.startMaxDate = new Date(getMoment(this.valueData.chart.x_axis.max).format()); const min = this.valueData.min;
this.endMinDate = new Date(getMoment(this.valueData.chart.x_axis.min).format()); const max = this.valueData.max;
this.endMaxDate = new Date(getMoment(this.valueData.chart.x_axis.max).format()); switch (this.selectedAttName.type) {
// 初始化: 讓日曆的範圍等於時間軸的範圍 case 'date':
this.startTime = this.startMinDate; // 除了 date 外雙向綁定為空
this.endTime = this.startMaxDate; this.valueStart = null;
this.valueEnd = null;
// 初始化: Calendar
this.startMinDate = new Date(getMoment(min).format());
this.startMaxDate = new Date(getMoment(max).format());
this.endMinDate = new Date(getMoment(min).format());
this.endMaxDate = new Date(getMoment(max).format());
// 初始化: 讓日曆的範圍等於時間軸的範圍
this.startTime = min;
this.endTime = max;
break;
default:
// date 雙向綁定為空
this.startTime = null;
this.endTime = null;
// 初始化InputNumber
this.valueStartMin = min;
this.valueStartMax = max;
this.valueEndMin = min;
this.valueEndMax = max;
// 初始化: 讓 InputNumber 的範圍等於時間軸的範圍
this.valueStart = min;
this.valueEnd = max;
break;
}
// 傳給後端
this.attValueTypeStartEnd; this.attValueTypeStartEnd;
// 建立圖表 // 建立圖表
this.createChart(); this.createChart();
} else { } else { // 選取類別型 table 的選項
// this.$emit('on-row-select', e.data); // this.$emit('on-row-select', e.data);
} }
}, },
@@ -295,8 +368,10 @@ export default {
const data = setLineChartData(valueData.chart.data, valueData.chart.x_axis.max, valueData.chart.x_axis.min); const data = setLineChartData(valueData.chart.data, valueData.chart.x_axis.max, valueData.chart.x_axis.min);
const xData = data.map(item => Math.round(item.x)); const xData = data.map(item => Math.round(item.x));
const isDateType = valueData.type === 'date'; const isDateType = valueData.type === 'date';
let setChartData= {};
let setChartOptions= {};
this.chartData = { setChartData = {
datasets: [ datasets: [
{ {
label: 'Attribute Value', label: 'Attribute Value',
@@ -311,7 +386,7 @@ export default {
} }
] ]
}; };
this.chartOptions = { setChartOptions = {
responsive: true, responsive: true,
maintainAspectRatio: false, maintainAspectRatio: false,
layout: { layout: {
@@ -367,8 +442,10 @@ export default {
}, },
}, },
}; };
if(!isDateType) this.chartData.labels = xData; if(!isDateType) setChartData.labels = xData;
if(isDateType) this.chartOptions.scales.x.type = 'time'; if(isDateType) setChartOptions.scales.x.type = 'time';
this.chartData = setChartData;
this.chartOptions = setChartOptions;
}, },
/** /**
* 滑塊改變的時候 * 滑塊改變的時候
@@ -380,11 +457,22 @@ export default {
let start = sliderData[e[0].toFixed()]; let start = sliderData[e[0].toFixed()];
let end = sliderData[e[1].toFixed()]; // 取得 index須為整數。 let end = sliderData[e[1].toFixed()]; // 取得 index須為整數。
this.startTime = new Date(start); switch (this.selectedAttName.type) {
this.endTime = new Date(end); case 'date':
// 重新設定 start end 日曆選取範圍 this.startTime = new Date(start);
this.endMinDate = new Date(start); this.endTime = new Date(end);
this.startMaxDate = new Date(end); // 重新設定 start end 日曆選取範圍
this.endMinDate = new Date(start);
this.startMaxDate = new Date(end);
break;
default:
this.valueStart = start;
this.valueEnd = end;
// 重新設定 start end 日曆選取範圍
this.valueEndMin = start;
this.valueStartMax = end;
break;
}
// 重新算圖 // 重新算圖
this.resizeMask(this.chartComplete); this.resizeMask(this.chartComplete);
// 執行 timeFrameStartEnd 才會改變數據 // 執行 timeFrameStartEnd 才會改變數據
@@ -392,13 +480,18 @@ export default {
}, },
/** /**
* 選取開始或結束時間時,要改變滑塊跟圖表 * 選取開始或結束時間時,要改變滑塊跟圖表
* @param {object} e Tue Jan 25 2022 00:00:00 GMT+0800 (台北標準時間) * @param {object} e Tue Jan 25 2022 00:00:00 GMT+0800 (台北標準時間) | Blur Event
* @param {string} direction start or end * @param {string} direction start or end
*/ */
sliderTimeRange(e, direction) { sliderValueRange(e, direction) {
// 找到最鄰近的 index時間格式: 毫秒時間戳 // 找到最鄰近的 index時間格式: 毫秒時間戳
let sliderData = this.sliderData; let sliderData = this.sliderData;
const targetTime = [new Date(this.attValueTypeStartEnd[0]).getTime(), new Date(this.attValueTypeStartEnd[1]).getTime()]; let isDateType = this.selectedAttName.type === 'date';
let targetTime = [];
if(isDateType) targetTime = [new Date(this.attValueTypeStartEnd[0]).getTime(), new Date(this.attValueTypeStartEnd[1]).getTime()];
else targetTime = [this.attValueTypeStartEnd[0], this.attValueTypeStartEnd[1]]
const closestIndexes = targetTime.map(target => { const closestIndexes = targetTime.map(target => {
let closestIndex = 0; let closestIndex = 0;
closestIndex = ((target - sliderData[0])/(sliderData[sliderData.length-1]-sliderData[0])) * sliderData.length; closestIndex = ((target - sliderData[0])/(sliderData[sliderData.length-1]-sliderData[0])) * sliderData.length;
@@ -406,12 +499,16 @@ export default {
result = result > this.selectRange ? this.selectRange : result; result = result > this.selectRange ? this.selectRange : result;
return result return result
}); });
// 改變滑塊 // 改變滑塊
this.selectArea = closestIndexes; this.selectArea = closestIndexes;
// 重新設定 start end 日曆選取範圍 // 重新設定 start end 日曆選取範圍
if(direction === 'start') this.endMinDate = e; let inputValue = Number(e.value.replace(/,/g, '')) ;
else if(direction === 'end') this.startMaxDate = e; if(direction === 'start') {
isDateType ? this.endMinDate = e : this.valueEndMin = inputValue;
}
else if(direction === 'end') {
isDateType ? this.endMinDate = e : this.valueStartMax = inputValue;
}
// 重新算圖 // 重新算圖
if(!isNaN(closestIndexes[0]) && !isNaN(closestIndexes[1])) this.resizeMask(this.chartComplete); if(!isNaN(closestIndexes[0]) && !isNaN(closestIndexes[1])) this.resizeMask(this.chartComplete);
else return; else return;

View File

@@ -39,6 +39,7 @@ import RadioButton from 'primevue/radiobutton';
import PickList from 'primevue/picklist'; import PickList from 'primevue/picklist';
import Timeline from 'primevue/timeline'; import Timeline from 'primevue/timeline';
import InputSwitch from 'primevue/inputswitch'; import InputSwitch from 'primevue/inputswitch';
import InputNumber from 'primevue/inputnumber';
import Chart from 'primevue/chart'; import Chart from 'primevue/chart';
import Slider from 'primevue/slider'; import Slider from 'primevue/slider';
import Calendar from 'primevue/calendar'; import Calendar from 'primevue/calendar';
@@ -90,6 +91,7 @@ app.component('RadioButton', RadioButton);
app.component('PickList', PickList); app.component('PickList', PickList);
app.component('Timeline', Timeline); app.component('Timeline', Timeline);
app.component('InputSwitch', InputSwitch); app.component('InputSwitch', InputSwitch);
app.component('InputNumber', InputNumber);
app.component('Chart', Chart); app.component('Chart', Chart);
app.component('Slider', Slider); app.component('Slider', Slider);
app.component('Calendar', Calendar); app.component('Calendar', Calendar);