-
-
Start date
-
+
+
+
+ Start time
+
+
+
~
+
+ End time
+
-
+
@@ -33,24 +35,58 @@ import { storeToRefs } from 'pinia';
import AllMapDataStore from '@/stores/allMapData.js';
import { Chart, registerables } from 'chart.js';
import 'chartjs-adapter-date-fns';
+import getMoment from 'moment';
export default{
setup() {
const allMapDataStore = AllMapDataStore();
- const { filterTimeframe } = storeToRefs(allMapDataStore);
+ const { filterTimeframe, selectTimeFrame } = storeToRefs(allMapDataStore);
- return {allMapDataStore, filterTimeframe}
+ return {allMapDataStore, filterTimeframe, selectTimeFrame }
},
data() {
return {
- selectRange: 300, // 更改 select 的切分數
- selectArea: [1,10],
- date: null,
+ selectRange: 1000, // 更改 select 的切分數
+ selectArea: null,
chart: null,
canvasId: null,
+ startTime: null,
+ endTime: null,
+ startMinDate: null,
+ startMaxDate: null,
+ endMinDate: null,
+ endMaxDate: null,
+ panelProps: {
+ onClick: (event) => {
+ event.stopPropagation();
+ },
+ },
+ datetime24h:null,
}
},
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(){
let data = this.filterTimeframe.data.map(i=>({x:i.x,y:i.y}))
@@ -164,7 +200,9 @@ export default{
x: {
type: 'time',
ticks: {
- color: '#0f172a',
+ autoSkip: false,
+ maxRotation: 0, // 不旋轉 lable 0~50
+ color: '#334155',
display: true,
},
grid: {
@@ -201,30 +239,67 @@ export default{
* @param {array} e [1, 100]
*/
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() {
+ // Chart.js
Chart.register(...registerables);
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;
+ }
}
diff --git a/src/components/Discover/SidebarFilter.vue b/src/components/Discover/SidebarFilter.vue
index 5b6b769..d24ce26 100644
--- a/src/components/Discover/SidebarFilter.vue
+++ b/src/components/Discover/SidebarFilter.vue
@@ -112,6 +112,7 @@ import ActAndSeq from '@/components/Discover/Filter/ActAndSeq.vue';
import Funnel from '@/components/Discover/Filter/Funnel.vue';
import Trace from '@/components/Discover/Filter/Trace.vue';
import Timeframes from '@/components/Discover/Filter/Timeframes.vue';
+import getMoment from 'moment';
export default {
props: ['sidebarFilter', 'filterTasks', 'filterStartToEnd', 'filterEndToStart', 'filterTimeframe', 'filterTrace'],
@@ -119,9 +120,9 @@ export default {
const loadingStore = LoadingStore();
const allMapDataStore = AllMapDataStore();
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() {
return {
@@ -186,7 +187,7 @@ export default {
},
filterEndToStartData: function() {
return this.isStartSelected ? this.setStartAndEndData(this.filterStartToEnd, this.rowData, 'sinks') : this.setActData(this.filterEndToStart);
- }
+ },
},
methods: {
/**
@@ -317,6 +318,12 @@ export default {
setRule(e) {
let label, type;
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){
case "contains-task":
@@ -347,7 +354,7 @@ export default {
case "started-in":
case "completed-in":
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"
break
case "trace-freq":
@@ -358,13 +365,14 @@ export default {
return {
type,
label,
- toggle:true,
+ toggle: true,
};
},
/**
* @param {boolean} massage true | false 清空選項
*/
reset(massage) {
+ // Sequence
this.selectFilterTask = null;
this.selectFilterStart = null;
this.selectFilterEnd = null;
@@ -374,9 +382,13 @@ export default {
this.isStartSelected = null;
this.isEndSelected = null;
this.isActAllTask = true;
+ // Timeframes
+ this.timeFrameStartEnd = null;
// Trace
- this.$refs.filterTraceView.showTraceId = null;
- this.$refs.filterTraceView.selectArea = [0, this.$refs.filterTraceView.traceTotal];
+ if (this.$refs.filterTraceView) {
+ this.$refs.filterTraceView.showTraceId = null;
+ this.$refs.filterTraceView.selectArea = [0, this.$refs.filterTraceView.traceTotal];
+ };
// 成功訊息
massage ? this.$toast.success('Reset Success.') : null;
},
@@ -384,7 +396,13 @@ export default {
async submit(){
let data;
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 的行為
// 若陣列為空,則跳出警告訊息
@@ -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 的行為
data = {
type: 'trace-freq',
@@ -451,6 +476,7 @@ export default {
}
// 將資料指向 Vue data 雙向綁定
const postData = Array.isArray(data) ? data : [data];
+ console.log(postData);
// 快速檢查每一 filter 規則是否為空集合
this.postRuleData = postData;
diff --git a/src/main.js b/src/main.js
index 6549274..bc19a15 100644
--- a/src/main.js
+++ b/src/main.js
@@ -40,7 +40,6 @@ import PickList from 'primevue/picklist';
import Timeline from 'primevue/timeline';
import InputSwitch from 'primevue/inputswitch';
import Chart from 'primevue/chart';
-// import 'chartjs-plugin-dragdata';
import Slider from 'primevue/slider';
import Calendar from 'primevue/calendar';
diff --git a/src/stores/allMapData.js b/src/stores/allMapData.js
index 71fbbb0..f731951 100644
--- a/src/stores/allMapData.js
+++ b/src/stores/allMapData.js
@@ -37,6 +37,7 @@ export default defineStore('allMapDataStore', {
isRuleData: [], // toggle button data
allFunnelData: [],
isUpdataFilter: false, // 是否成功儲存 Filter 檔
+ selectTimeFrame: [], // user select time start and end
}),
getters: {
processMap: state => {
@@ -304,6 +305,6 @@ export default defineStore('allMapDataStore', {
await delay(500);
$toast.default('Failed to updata an Existing Filter.',{position: 'bottom'});
}
- }
+ },
},
})