diff --git a/src/components/Discover/Filter/Funnel.vue b/src/components/Discover/Filter/Funnel.vue index deb924e..d7909c9 100644 --- a/src/components/Discover/Filter/Funnel.vue +++ b/src/components/Discover/Filter/Funnel.vue @@ -26,7 +26,6 @@ - this.postRuleData:{{this.postRuleData}} diff --git a/src/components/Discover/Filter/Timeframes.vue b/src/components/Discover/Filter/Timeframes.vue index 190fe23..b0f9d62 100644 --- a/src/components/Discover/Filter/Timeframes.vue +++ b/src/components/Discover/Filter/Timeframes.vue @@ -6,25 +6,27 @@ info

Select or fill in a time range.

-
+
-
-
+
+
- -
- +
-
- {{ selectArea }}
-
-
- 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'}); } - } + }, }, })