Discover: SidebarFilter Timeframes Apply and create Map done.
This commit is contained in:
@@ -26,7 +26,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Timeline>
|
</Timeline>
|
||||||
this.postRuleData:{{this.postRuleData}}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Button -->
|
<!-- Button -->
|
||||||
|
|||||||
@@ -6,25 +6,27 @@
|
|||||||
<span class="material-symbols-outlined mr-2 text-base">info</span>
|
<span class="material-symbols-outlined mr-2 text-base">info</span>
|
||||||
<p>Select or fill in a time range.</p>
|
<p>Select or fill in a time range.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="chartContainer">
|
<div class="chartContainer h-3/5 relative">
|
||||||
<canvas id="chartCanvasId"></canvas>
|
<canvas id="chartCanvasId"></canvas>
|
||||||
<div id="chart-mask-left"></div>
|
<div id="chart-mask-left" class="absolute bg-neutral-10/50"></div>
|
||||||
<div id="chart-mask-right"></div>
|
<div id="chart-mask-right" class="absolute bg-neutral-10/50"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="px-2 py-3">
|
||||||
<div class="px-2">
|
|
||||||
<!-- <Slider v-model="selectArea" :step="1" :min="1" :max="timeFrameTotal" range class="mx-2"/> -->
|
|
||||||
<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)"/>
|
||||||
<br/>
|
|
||||||
{{ selectArea }}<br/>
|
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Calendar group -->
|
||||||
|
<div class="flex justify-center items-center space-x-2 w-full">
|
||||||
<div>
|
<div>
|
||||||
<div @click.stop.prevent="">
|
<span class="block mb-2">Start time</span>
|
||||||
<span class="block">Start date</span>
|
<Calendar v-model="startTime" dateFormat="yy/mm/dd" :panelProps="panelProps" :minDate="startMinDate" :maxDate="startMaxDate" showTime showIcon hourFormat="24" @date-select="sliderTimeRange($event, 'start')"/>
|
||||||
<Calendar v-model="date" dateFormat="dd/mm/yy" />
|
|
||||||
</div>
|
</div>
|
||||||
<!-- <Calendar v-model="date" @date-select.stop="eee($event)" /> -->
|
<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')"/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- End calendar group -->
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -33,24 +35,58 @@ import { storeToRefs } from 'pinia';
|
|||||||
import AllMapDataStore from '@/stores/allMapData.js';
|
import AllMapDataStore from '@/stores/allMapData.js';
|
||||||
import { Chart, registerables } from 'chart.js';
|
import { Chart, registerables } from 'chart.js';
|
||||||
import 'chartjs-adapter-date-fns';
|
import 'chartjs-adapter-date-fns';
|
||||||
|
import getMoment from 'moment';
|
||||||
|
|
||||||
export default{
|
export default{
|
||||||
setup() {
|
setup() {
|
||||||
const allMapDataStore = AllMapDataStore();
|
const allMapDataStore = AllMapDataStore();
|
||||||
const { filterTimeframe } = storeToRefs(allMapDataStore);
|
const { filterTimeframe, selectTimeFrame } = storeToRefs(allMapDataStore);
|
||||||
|
|
||||||
return {allMapDataStore, filterTimeframe}
|
return {allMapDataStore, filterTimeframe, selectTimeFrame }
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
selectRange: 300, // 更改 select 的切分數
|
selectRange: 1000, // 更改 select 的切分數
|
||||||
selectArea: [1,10],
|
selectArea: null,
|
||||||
date: null,
|
|
||||||
chart: null,
|
chart: null,
|
||||||
canvasId: null,
|
canvasId: null,
|
||||||
|
startTime: null,
|
||||||
|
endTime: null,
|
||||||
|
startMinDate: null,
|
||||||
|
startMaxDate: null,
|
||||||
|
endMinDate: null,
|
||||||
|
endMaxDate: null,
|
||||||
|
panelProps: {
|
||||||
|
onClick: (event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
datetime24h:null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
// user select time start and end
|
||||||
|
timeFrameStartEnd: function() {
|
||||||
|
let start = getMoment(this.startTime).format('YYYY-MM-DDTHH:mm:ss');
|
||||||
|
let end = getMoment(this.endTime).format('YYYY-MM-DDTHH:mm:ss');
|
||||||
|
this.selectTimeFrame = [start ,end]; // 傳給後端的資料
|
||||||
|
|
||||||
|
return [start ,end];
|
||||||
|
},
|
||||||
|
// 找出 slidrData,時間格式:毫秒時間戳
|
||||||
|
sliderData: function() {
|
||||||
|
let xAxisMin = new Date(this.filterTimeframe.x_axis.min).getTime();
|
||||||
|
let xAxisMax = new Date(this.filterTimeframe.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;
|
||||||
|
},
|
||||||
// 加入最大、最小值
|
// 加入最大、最小值
|
||||||
timeFrameData: function(){
|
timeFrameData: function(){
|
||||||
let data = this.filterTimeframe.data.map(i=>({x:i.x,y:i.y}))
|
let data = this.filterTimeframe.data.map(i=>({x:i.x,y:i.y}))
|
||||||
@@ -164,7 +200,9 @@ export default{
|
|||||||
x: {
|
x: {
|
||||||
type: 'time',
|
type: 'time',
|
||||||
ticks: {
|
ticks: {
|
||||||
color: '#0f172a',
|
autoSkip: false,
|
||||||
|
maxRotation: 0, // 不旋轉 lable 0~50
|
||||||
|
color: '#334155',
|
||||||
display: true,
|
display: true,
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
@@ -201,30 +239,67 @@ export default{
|
|||||||
* @param {array} e [1, 100]
|
* @param {array} e [1, 100]
|
||||||
*/
|
*/
|
||||||
changeSelectArea(e) {
|
changeSelectArea(e) {
|
||||||
this.resizeMask(this.chart)
|
// 日曆改變時,滑塊跟著改變
|
||||||
|
let sliderData = this.sliderData;
|
||||||
|
this.startTime = new Date(sliderData[e[0]]);
|
||||||
|
this.endTime = new Date(sliderData[e[1]]);
|
||||||
|
// 重新設定 start end 日曆選取範圍
|
||||||
|
this.endMinDate = new Date(sliderData[e[0]]);
|
||||||
|
this.startMaxDate = new Date(sliderData[e[1]]);
|
||||||
|
// 重新算圖
|
||||||
|
this.resizeMask(this.chart);
|
||||||
|
// 執行 timeFrameStartEnd 才會改變數據
|
||||||
|
this.timeFrameStartEnd;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 選取開始或結束時間時,要改變滑塊根圖表
|
||||||
|
* @param {object} e Tue Jan 25 2022 00:00:00 GMT+0800 (台北標準時間)
|
||||||
|
* @param {string} direction start or end
|
||||||
|
*/
|
||||||
|
sliderTimeRange(e, direction) {
|
||||||
|
// 找到最鄰近的 index,時間格式: 毫秒時間戳
|
||||||
|
let sliderData = this.sliderData;
|
||||||
|
const targetTime = [new Date(this.timeFrameStartEnd[0]).getTime(), new Date(this.timeFrameStartEnd[1]).getTime()];
|
||||||
|
const closestIndexes = targetTime.map(target => {
|
||||||
|
let closestIndex = 0;
|
||||||
|
closestIndex = ((target - sliderData[0])/(sliderData[sliderData.length-1]-sliderData[0])) * sliderData.length;
|
||||||
|
return Math.round(Math.abs(closestIndex));
|
||||||
|
});
|
||||||
|
|
||||||
|
// 改變滑塊
|
||||||
|
this.selectArea = closestIndexes;
|
||||||
|
// 重新設定 start end 日曆選取範圍
|
||||||
|
if(direction === 'start') this.endMinDate = e;
|
||||||
|
else if(direction === 'end') this.startMaxDate = e;
|
||||||
|
// 重新算圖
|
||||||
|
if(!isNaN(closestIndexes[0]) && !isNaN(closestIndexes[1])) this.resizeMask(this.chart);
|
||||||
|
else return;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
// Chart.js
|
||||||
Chart.register(...registerables);
|
Chart.register(...registerables);
|
||||||
this.createChart();
|
this.createChart();
|
||||||
this.selectArea = [0, this.selectRange]
|
// Slider
|
||||||
|
this.selectArea = [0, this.selectRange];
|
||||||
|
// Calendar
|
||||||
|
this.startMinDate = new Date(getMoment(this.filterTimeframe.x_axis.min).startOf('day').local().format());
|
||||||
|
this.startMaxDate = new Date(getMoment(this.filterTimeframe.x_axis.max).startOf('day').local().format());
|
||||||
|
this.endMinDate = new Date(getMoment(this.filterTimeframe.x_axis.min).startOf('day').local().format());
|
||||||
|
this.endMaxDate = new Date(getMoment(this.filterTimeframe.x_axis.max).startOf('day').local().format());
|
||||||
|
// 讓日曆的範圍等於時間軸的範圍
|
||||||
|
this.startTime = this.startMinDate;
|
||||||
|
this.endTime = this.startMaxDate;
|
||||||
|
this.timeFrameStartEnd;
|
||||||
},
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
this.selectArea = [0, this.selectRange];
|
||||||
|
this.resizeMask(this.chart);
|
||||||
|
this.startTime = new Date(getMoment(this.filterTimeframe.x_axis.min).startOf('day').local().format());
|
||||||
|
this.endTime = new Date(getMoment(this.filterTimeframe.x_axis.max).startOf('day').local().format());
|
||||||
|
this.timeFrameStartEnd;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
.chart-container {
|
|
||||||
position: relative;
|
|
||||||
margin: 2rem 4rem;
|
|
||||||
padding: 20px 40px;
|
|
||||||
border: 1px solid black;
|
|
||||||
width: 60%;
|
|
||||||
height: 60%;
|
|
||||||
}
|
|
||||||
#chartCanvas {
|
|
||||||
border: 1px solid red !important;
|
|
||||||
}
|
|
||||||
#chart-mask-left, #chart-mask-right {
|
|
||||||
position: absolute;
|
|
||||||
background-color: rgb(255, 255, 255, 0.5);
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ import ActAndSeq from '@/components/Discover/Filter/ActAndSeq.vue';
|
|||||||
import Funnel from '@/components/Discover/Filter/Funnel.vue';
|
import Funnel from '@/components/Discover/Filter/Funnel.vue';
|
||||||
import Trace from '@/components/Discover/Filter/Trace.vue';
|
import Trace from '@/components/Discover/Filter/Trace.vue';
|
||||||
import Timeframes from '@/components/Discover/Filter/Timeframes.vue';
|
import Timeframes from '@/components/Discover/Filter/Timeframes.vue';
|
||||||
|
import getMoment from 'moment';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['sidebarFilter', 'filterTasks', 'filterStartToEnd', 'filterEndToStart', 'filterTimeframe', 'filterTrace'],
|
props: ['sidebarFilter', 'filterTasks', 'filterStartToEnd', 'filterEndToStart', 'filterTimeframe', 'filterTrace'],
|
||||||
@@ -119,9 +120,9 @@ export default {
|
|||||||
const loadingStore = LoadingStore();
|
const loadingStore = LoadingStore();
|
||||||
const allMapDataStore = AllMapDataStore();
|
const allMapDataStore = AllMapDataStore();
|
||||||
const { isLoading } = storeToRefs(loadingStore);
|
const { isLoading } = storeToRefs(loadingStore);
|
||||||
const { hasResultRule, temporaryData, postRuleData, ruleData, isRuleData} = storeToRefs(allMapDataStore);
|
const { hasResultRule, temporaryData, postRuleData, ruleData, isRuleData, selectTimeFrame } = storeToRefs(allMapDataStore);
|
||||||
|
|
||||||
return { isLoading, hasResultRule, temporaryData, postRuleData, ruleData, isRuleData, allMapDataStore }
|
return { isLoading, hasResultRule, temporaryData, postRuleData, ruleData, isRuleData, allMapDataStore, selectTimeFrame }
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -186,7 +187,7 @@ export default {
|
|||||||
},
|
},
|
||||||
filterEndToStartData: function() {
|
filterEndToStartData: function() {
|
||||||
return this.isStartSelected ? this.setStartAndEndData(this.filterStartToEnd, this.rowData, 'sinks') : this.setActData(this.filterEndToStart);
|
return this.isStartSelected ? this.setStartAndEndData(this.filterStartToEnd, this.rowData, 'sinks') : this.setActData(this.filterEndToStart);
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
/**
|
/**
|
||||||
@@ -317,6 +318,12 @@ export default {
|
|||||||
setRule(e) {
|
setRule(e) {
|
||||||
let label, type;
|
let label, type;
|
||||||
const includeStr = e.is_exclude?" Exclude ":" Include ";
|
const includeStr = e.is_exclude?" Exclude ":" Include ";
|
||||||
|
let containmentMap = {
|
||||||
|
'occurred-in' : 'Contained in',
|
||||||
|
'started-in' : 'Started in',
|
||||||
|
'completed-in' : 'Ended in',
|
||||||
|
'occurred-around' : 'Active in'
|
||||||
|
};
|
||||||
|
|
||||||
switch(e.type){
|
switch(e.type){
|
||||||
case "contains-task":
|
case "contains-task":
|
||||||
@@ -347,7 +354,7 @@ export default {
|
|||||||
case "started-in":
|
case "started-in":
|
||||||
case "completed-in":
|
case "completed-in":
|
||||||
case "occurred-around":
|
case "occurred-around":
|
||||||
label = `${e.type} from ${moment(e.start).format("YYYY-MM-DD HH:mm:ss")} to ${moment(e.end).format("YYYY-MM-DD HH:mm:ss")} ${includeStr}`
|
label = `${containmentMap[e.type]} from ${getMoment(e.start).format("YYYY-MM-DD HH:mm:ss")} to ${getMoment(e.end).format("YYYY-MM-DD HH:mm:ss")} ${includeStr}`
|
||||||
type = "Timeframe"
|
type = "Timeframe"
|
||||||
break
|
break
|
||||||
case "trace-freq":
|
case "trace-freq":
|
||||||
@@ -358,13 +365,14 @@ export default {
|
|||||||
return {
|
return {
|
||||||
type,
|
type,
|
||||||
label,
|
label,
|
||||||
toggle:true,
|
toggle: true,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @param {boolean} massage true | false 清空選項
|
* @param {boolean} massage true | false 清空選項
|
||||||
*/
|
*/
|
||||||
reset(massage) {
|
reset(massage) {
|
||||||
|
// Sequence
|
||||||
this.selectFilterTask = null;
|
this.selectFilterTask = null;
|
||||||
this.selectFilterStart = null;
|
this.selectFilterStart = null;
|
||||||
this.selectFilterEnd = null;
|
this.selectFilterEnd = null;
|
||||||
@@ -374,9 +382,13 @@ export default {
|
|||||||
this.isStartSelected = null;
|
this.isStartSelected = null;
|
||||||
this.isEndSelected = null;
|
this.isEndSelected = null;
|
||||||
this.isActAllTask = true;
|
this.isActAllTask = true;
|
||||||
|
// Timeframes
|
||||||
|
this.timeFrameStartEnd = null;
|
||||||
// Trace
|
// Trace
|
||||||
|
if (this.$refs.filterTraceView) {
|
||||||
this.$refs.filterTraceView.showTraceId = null;
|
this.$refs.filterTraceView.showTraceId = null;
|
||||||
this.$refs.filterTraceView.selectArea = [0, this.$refs.filterTraceView.traceTotal];
|
this.$refs.filterTraceView.selectArea = [0, this.$refs.filterTraceView.traceTotal];
|
||||||
|
};
|
||||||
// 成功訊息
|
// 成功訊息
|
||||||
massage ? this.$toast.success('Reset Success.') : null;
|
massage ? this.$toast.success('Reset Success.') : null;
|
||||||
},
|
},
|
||||||
@@ -384,7 +396,13 @@ export default {
|
|||||||
async submit(){
|
async submit(){
|
||||||
let data;
|
let data;
|
||||||
let sele = this.selectValue;
|
let sele = this.selectValue;
|
||||||
let isExclude = sele[5] === 'Exclude' ? true : false
|
let isExclude = sele[5] === 'Exclude' ? true : false;
|
||||||
|
let containmentMap = {
|
||||||
|
'Contained in': 'occurred-in',
|
||||||
|
'Started in': 'started-in',
|
||||||
|
'Ended in': 'completed-in',
|
||||||
|
'Active in': 'occurred-around'
|
||||||
|
};
|
||||||
|
|
||||||
// Filter Type 選 Sequence 的行為
|
// Filter Type 選 Sequence 的行為
|
||||||
// 若陣列為空,則跳出警告訊息
|
// 若陣列為空,則跳出警告訊息
|
||||||
@@ -441,6 +459,13 @@ export default {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
} else if(sele[0] === 'Timeframes'){ // Filter Type 選 Timeframes 的行為
|
||||||
|
data = {
|
||||||
|
type: containmentMap[sele[6]],
|
||||||
|
start: this.selectTimeFrame[0],
|
||||||
|
end: this.selectTimeFrame[1],
|
||||||
|
is_exclude: isExclude,
|
||||||
|
}
|
||||||
} else if(sele[0] === 'Trace'){ // Filter Type 選 Trace 的行為
|
} else if(sele[0] === 'Trace'){ // Filter Type 選 Trace 的行為
|
||||||
data = {
|
data = {
|
||||||
type: 'trace-freq',
|
type: 'trace-freq',
|
||||||
@@ -451,6 +476,7 @@ export default {
|
|||||||
}
|
}
|
||||||
// 將資料指向 Vue data 雙向綁定
|
// 將資料指向 Vue data 雙向綁定
|
||||||
const postData = Array.isArray(data) ? data : [data];
|
const postData = Array.isArray(data) ? data : [data];
|
||||||
|
console.log(postData);
|
||||||
|
|
||||||
// 快速檢查每一 filter 規則是否為空集合
|
// 快速檢查每一 filter 規則是否為空集合
|
||||||
this.postRuleData = postData;
|
this.postRuleData = postData;
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ 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 Chart from 'primevue/chart';
|
import Chart from 'primevue/chart';
|
||||||
// import 'chartjs-plugin-dragdata';
|
|
||||||
import Slider from 'primevue/slider';
|
import Slider from 'primevue/slider';
|
||||||
import Calendar from 'primevue/calendar';
|
import Calendar from 'primevue/calendar';
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ export default defineStore('allMapDataStore', {
|
|||||||
isRuleData: [], // toggle button data
|
isRuleData: [], // toggle button data
|
||||||
allFunnelData: [],
|
allFunnelData: [],
|
||||||
isUpdataFilter: false, // 是否成功儲存 Filter 檔
|
isUpdataFilter: false, // 是否成功儲存 Filter 檔
|
||||||
|
selectTimeFrame: [], // user select time start and end
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
processMap: state => {
|
processMap: state => {
|
||||||
@@ -304,6 +305,6 @@ export default defineStore('allMapDataStore', {
|
|||||||
await delay(500);
|
await delay(500);
|
||||||
$toast.default('Failed to updata an Existing Filter.',{position: 'bottom'});
|
$toast.default('Failed to updata an Existing Filter.',{position: 'bottom'});
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user