Issue #165: Done.

This commit is contained in:
chiayin
2023-10-11 18:50:46 +08:00
parent 6e0d5b15fe
commit 1cfbddf510
6 changed files with 137 additions and 50 deletions

View File

@@ -102,6 +102,9 @@ export default {
await this.allMapDataStore.addTempFilterId(); await this.allMapDataStore.addTempFilterId();
await this.allMapDataStore.getAllMapData(); await this.allMapDataStore.getAllMapData();
await this.allMapDataStore.getAllTrace(); // SidebarTrace 要連動 await this.allMapDataStore.getAllTrace(); // SidebarTrace 要連動
if(this.temporaryData[0]?.type) {
this.allMapDataStore.traceId = await this.allMapDataStore.traces[0]?.id;
}
await this.$emit('submit-all'); await this.$emit('submit-all');
this.isLoading = false; this.isLoading = false;
this.$toast.success('Filter(s) applied.'); this.$toast.success('Filter(s) applied.');

View File

@@ -50,8 +50,8 @@
<div id="cyTrace" ref="cyTrace" class="h-full min-w-full relative"></div> <div id="cyTrace" ref="cyTrace" class="h-full min-w-full relative"></div>
</div> </div>
</div> </div>
<div class="overflow-y-auto overflow-x-auto scrollbar h-[calc(100%_-_264px)]"> <div class="overflow-y-auto overflow-x-auto scrollbar h-[calc(100%_-_264px)] infiniteTable" @scroll="handleScroll">
<DataTable :value="cases" showGridlines tableClass="text-sm" breakpoint="0"> <DataTable :value="infiniteData" showGridlines tableClass="text-sm" breakpoint="0">
<Column field="id" header="Case ID" ></Column> <Column field="id" header="Case ID" ></Column>
<Column field="started_at" header="Start time" ></Column> <Column field="started_at" header="Start time" ></Column>
<Column field="completed_at" header="End time" ></Column> <Column field="completed_at" header="End time" ></Column>
@@ -72,10 +72,10 @@ export default {
setup() { setup() {
const allMapDataStore = AllMapDataStore(); const allMapDataStore = AllMapDataStore();
const loadingStore = LoadingStore(); const loadingStore = LoadingStore();
const { traces, traceTaskSeq, cases } = storeToRefs(allMapDataStore); const { infinit404, infiniteStart, traces, traceTaskSeq, cases } = storeToRefs(allMapDataStore);
const { isLoading } = storeToRefs(loadingStore); const { isLoading } = storeToRefs(loadingStore);
return {allMapDataStore, traces, traceTaskSeq, cases, isLoading} return {allMapDataStore, infinit404, infiniteStart, traces, traceTaskSeq, cases, isLoading}
}, },
data() { data() {
return { return {
@@ -84,6 +84,8 @@ export default {
edges:[], edges:[],
}, },
showTraceId: null, showTraceId: null,
infinitMaxItems: false,
infiniteData: [],
chartOptions: null, chartOptions: null,
selectArea: [0, 1] selectArea: [0, 1]
} }
@@ -136,7 +138,14 @@ export default {
selectArea: function(newValue) { selectArea: function(newValue) {
let roundValue = Math.round(newValue[1].toFixed()); let roundValue = Math.round(newValue[1].toFixed());
if(newValue[1] !== roundValue) this.selectArea[1] = roundValue; if(newValue[1] !== roundValue) this.selectArea[1] = roundValue;
} },
infinite404: function(newValue) {
if(newValue === 404) this.infinitMaxItems = true;
},
showTraceId: function(newValue, oldValue) {
let isScrollTop = document.querySelector('.infiniteTable');
if(isScrollTop && typeof isScrollTop.scrollTop !== 'undefined') if(newValue !== oldValue) isScrollTop.scrollTop = 0;
},
}, },
methods: { methods: {
/** /**
@@ -210,9 +219,12 @@ export default {
*/ */
async switchCaseData(id, count) { async switchCaseData(id, count) {
if(count >= 1000) this.isLoading = true; if(count >= 1000) this.isLoading = true;
this.infinit404 = null;
this.infinitMaxItems = false;
this.showTraceId = id; this.showTraceId = id;
this.infiniteStart = 0;
this.allMapDataStore.traceId = id; this.allMapDataStore.traceId = id;
await this.allMapDataStore.getTraceDetail(); this.infiniteData = await this.allMapDataStore.getTraceDetail();
this.createCy(); this.createCy();
this.isLoading = false; this.isLoading = false;
}, },
@@ -265,6 +277,30 @@ export default {
this.setEdgesData(); this.setEdgesData();
cytoscapeMapTrace(this.processMap.nodes, this.processMap.edges, graphId); cytoscapeMapTrace(this.processMap.nodes, this.processMap.edges, graphId);
}, },
/**
* 無限滾動: 監聽 scroll 有沒有滾到底部
* @param {element} event
*/
handleScroll(event) {
if(this.infinitMaxItems || this.cases.length < 20) return;
const container = event.target;
const overScrollHeight = container.scrollTop + container.clientHeight >= container.scrollHeight;
if(overScrollHeight) this.fetchData();
},
/**
* 無限滾動: 滾到底後,要載入數據
*/
async fetchData() {
try {
this.infiniteStart += 20;
await this.allMapDataStore.getTraceDetail();
this.infiniteData = [...this.infiniteData, ...this.cases];
} catch(error) {
console.error('Failed to load data:', error);
}
}
}, },
mounted() { mounted() {
this.setNodesData(); this.setNodesData();

View File

@@ -1,5 +1,5 @@
<template> <template>
<Sidebar :visible="sidebarFilter" :closeIcon="'pi pi-chevron-left'" :modal="false" position="left" :dismissable="true" :baseZIndex="15" class="!w-11/12 !bg-neutral-100" @hide="hide()"> <Sidebar :visible="sidebarFilter" :closeIcon="'pi pi-chevron-left'" :modal="false" position="left" :dismissable="true" :baseZIndex="15" class="!w-11/12 !bg-neutral-100">
<template #header> <template #header>
<ul class="flex space-x-4"> <ul class="flex space-x-4">
<li class="h1 border-r-2 border-neutral-300 pr-4 cursor-pointer hover:text-neutral-900 hover:duration-700" @click="switchTab('filter')" :class="tab === 'filter'? 'text-neutral-900': 'text-neutral-500'" id="tabFilter">Filter</li> <li class="h1 border-r-2 border-neutral-300 pr-4 cursor-pointer hover:text-neutral-900 hover:duration-700" @click="switchTab('filter')" :class="tab === 'filter'? 'text-neutral-900': 'text-neutral-500'" id="tabFilter">Filter</li>
@@ -498,10 +498,12 @@ export default {
} }
break; break;
case 'Trace': // Filter Type 選 Trace 的行為 case 'Trace': // Filter Type 選 Trace 的行為
let lowerIndex = this.$refs.filterTraceView.selectArea[0];
let upperIndex = this.$refs.filterTraceView.selectArea[1]-1;
data = { data = {
type: 'trace-freq', type: 'trace-freq',
lower: this.$refs.filterTraceView.selectArea[0]+1, lower: this.allMapDataStore.traces[lowerIndex].id,
upper: this.$refs.filterTraceView.selectArea[1], upper: this.allMapDataStore.traces[upperIndex].id,
is_exclude: isExclude, is_exclude: isExclude,
}; };
break; break;
@@ -552,14 +554,6 @@ export default {
sumbitAll() { sumbitAll() {
this.$emit('submit-all'); this.$emit('submit-all');
}, },
/**
* hide map
*/
hide() {
// 因 trace api 連動,所以關閉側邊欄時讓數值歸 1
this.allMapDataStore.traceId = 1;
this.allMapDataStore.getTraceDetail();
}
}, },
} }
</script> </script>

View File

@@ -1,5 +1,5 @@
<template> <template>
<Sidebar :visible="sidebarTraces" :closeIcon="'pi pi-chevron-left'" :modal="false" position="left" :dismissable="true" class="!w-11/12" @show="show()" @hide="hide()"> <Sidebar :visible="sidebarTraces" :closeIcon="'pi pi-chevron-left'" :modal="false" position="left" :dismissable="true" class="!w-11/12" @show="show()">
<template #header> <template #header>
<p class="h1">Traces</p> <p class="h1">Traces</p>
</template> </template>
@@ -41,8 +41,8 @@
<div id="cyTrace" ref="cyTrace" class="h-full min-w-full relative"></div> <div id="cyTrace" ref="cyTrace" class="h-full min-w-full relative"></div>
</div> </div>
</div> </div>
<div class="overflow-y-auto overflow-x-auto scrollbar h-[calc(100%_-_264px)]"> <div class="overflow-y-auto overflow-x-auto scrollbar h-[calc(100%_-_264px)] infiniteTable" @scroll="handleScroll">
<DataTable :value="cases" showGridlines tableClass="text-sm" breakpoint="0"> <DataTable :value="infiniteData" showGridlines tableClass="text-sm" breakpoint="0">
<Column field="id" header="Case ID" ></Column> <Column field="id" header="Case ID" ></Column>
<Column field="started_at" header="Start time" ></Column> <Column field="started_at" header="Start time" ></Column>
<Column field="completed_at" header="End time" ></Column> <Column field="completed_at" header="End time" ></Column>
@@ -52,21 +52,17 @@
</div> </div>
</Sidebar> </Sidebar>
</template> </template>
<script> <script>
import cytoscapeMapTrace from '@/module/cytoscapeMapTrace.js'; import { storeToRefs } from 'pinia';
import AllMapDataStore from '@/stores/allMapData.js'; import AllMapDataStore from '@/stores/allMapData.js';
import cytoscapeMapTrace from '@/module/cytoscapeMapTrace.js';
export default { export default {
props: { props: ['sidebarTraces'],
sidebarTraces: Boolean,
traces: Array,
traceTaskSeq: Array,
cases: Array,
},
setup() { setup() {
const allMapDataStore = AllMapDataStore(); const allMapDataStore = AllMapDataStore();
return {allMapDataStore} const { infinit404, infiniteStart, traceId, traces, traceTaskSeq, cases, infiniteFirstCases } = storeToRefs(allMapDataStore);
return {allMapDataStore, infinit404, infiniteStart, traceId, traces, traceTaskSeq, cases, infiniteFirstCases}
}, },
data() { data() {
return { return {
@@ -74,7 +70,9 @@ export default {
nodes:[], nodes:[],
edges:[], edges:[],
}, },
showTraceId: 1, showTraceId: null,
infinitMaxItems: false,
infiniteData: [],
} }
}, },
computed: { computed: {
@@ -93,7 +91,25 @@ export default {
ratio: this.getPercentLabel(trace.count / sum), ratio: this.getPercentLabel(trace.count / sum),
}; };
}).sort((x, y) => x.id - y.id); }).sort((x, y) => x.id - y.id);
}
}, },
watch: {
infinite404: function(newValue) {
if(newValue === 404) this.infinitMaxItems = true;
},
traceId: {
handler(newValue) {
this.showTraceId = newValue;
},
immediate: true
},
showTraceId: function(newValue, oldValue) {
let isScrollTop = document.querySelector('.infiniteTable');
if(isScrollTop && typeof isScrollTop.scrollTop !== 'undefined') if(newValue !== oldValue) isScrollTop.scrollTop = 0;
},
infiniteFirstCases: function(newValue){
if(this.infiniteFirstCases) this.infiniteData = JSON.parse(JSON.stringify(newValue));
}
}, },
methods: { methods: {
/** /**
@@ -117,7 +133,11 @@ export default {
* @param {number} id * @param {number} id
*/ */
async switchCaseData(id, count) { async switchCaseData(id, count) {
if(count >= 1000) this.isLoading = true;
this.infinit404 = null;
this.infinitMaxItems = false;
this.showTraceId = id; this.showTraceId = id;
this.infiniteStart = 0;
this.$emit('switch-Trace-Id', {id: this.showTraceId, count: count}); this.$emit('switch-Trace-Id', {id: this.showTraceId, count: count});
}, },
/** /**
@@ -172,20 +192,43 @@ export default {
/** /**
* create map * create map
*/ */
show() { async show() {
// 因 trace api 連動,所以關閉側邊欄時讓數值歸 traces 第一筆 id
this.showTraceId = await this.traces[0]?.id;
this.infiniteStart = await 0;
await this.allMapDataStore.getTraceDetail();
this.setNodesData(); this.setNodesData();
this.setEdgesData(); this.setEdgesData();
this.createCy(); this.createCy();
}, },
/** /**
* hide map * 無限滾動: 監聽 scroll 有沒有滾到底部
* @param {element} event
*/ */
hide() { handleScroll(event) {
// 因 trace api 連動,所以關閉側邊欄時讓數值歸 1 if(this.infinitMaxItems || this.cases.length < 20) return;
this.showTraceId = 1;
this.allMapDataStore.getTraceDetail(); const container = event.target;
const overScrollHeight = container.scrollTop + container.clientHeight >= container.scrollHeight;
if(overScrollHeight) this.fetchData();
},
/**
* 無限滾動: 滾到底後,要載入數據
*/
async fetchData() {
try {
this.infiniteStart += 20;
await this.allMapDataStore.getTraceDetail();
this.infiniteData = [...this.infiniteData, ...this.cases];
} catch(error) {
console.error('Failed to load data:', error);
}
} }
}, },
async created() {
this.allMapDataStore.getTraceDetail();
}
} }
</script> </script>

View File

@@ -38,6 +38,8 @@ export default defineStore('allMapDataStore', {
allFunnelData: [], allFunnelData: [],
isUpdataFilter: false, // 是否成功儲存 Filter 檔 isUpdataFilter: false, // 是否成功儲存 Filter 檔
selectTimeFrame: [], // user select time start and end selectTimeFrame: [], // user select time start and end
infinite404: null, // 無限滾動式是否到底要換頁
infiniteStart: 0, // 無限滾動 case 開始的數字
}), }),
getters: { getters: {
processMap: state => { processMap: state => {
@@ -58,6 +60,9 @@ export default defineStore('allMapDataStore', {
cases: state => { cases: state => {
return state.allCase; return state.allCase;
}, },
infiniteFirstCases: state => {
if(state.infiniteStart === 0) return state.allCase;
},
traceTaskSeq: state => { traceTaskSeq: state => {
return state.allTraceTaskSeq; return state.allTraceTaskSeq;
}, },
@@ -153,12 +158,14 @@ export default defineStore('allMapDataStore', {
let traceId = this.traceId; let traceId = this.traceId;
let tempFilterId = this.tempFilterId; let tempFilterId = this.tempFilterId;
let createfilterId = this.createFilterId; let createfilterId = this.createFilterId;
let start = this.infiniteStart;
let api = ''; let api = '';
// 先判斷暫存 再判斷 filter 最後 log // 先判斷暫存 再判斷 filter 最後 log
if(tempFilterId != null) api = `/api/temp-filters/${tempFilterId}/traces/${traceId}`; if(tempFilterId != null) api = `/api/temp-filters/${tempFilterId}/traces/${traceId}?start=${start}&page_size=20`;
else if(createfilterId!= null) api = `/api/filters/${createfilterId}/traces/${traceId}`; else if(createfilterId!= null) api = `/api/filters/${createfilterId}/traces/${traceId}?start=${start}&page_size=20`;
else api = `/api/logs/${logId}/traces/${traceId}`; else api = `/api/logs/${logId}/traces/${traceId}?start=${start}&page_size=20`;
try { try {
const response = await this.$axios.get(api); const response = await this.$axios.get(api);
@@ -169,9 +176,12 @@ export default defineStore('allMapDataStore', {
c.completed_at = moment(c.completed_at).format('YYYY-MM-DD HH:MM'); c.completed_at = moment(c.completed_at).format('YYYY-MM-DD HH:MM');
return this.allCase; return this.allCase;
}); });
return this.allCase;
// if(this.httpStatus < 300) loading.isLoading = false; // if(this.httpStatus < 300) loading.isLoading = false;
} catch(error) { } catch(error) {
if(error.response.status === 404) this.infinite404 = 404;
else {
this.httpStatus = error.request.status; this.httpStatus = error.request.status;
await delay(); await delay();
loading.isLoading = true; loading.isLoading = true;
@@ -179,6 +189,7 @@ export default defineStore('allMapDataStore', {
loading.isLoading = false; loading.isLoading = false;
await delay(500); await delay(500);
$toast.default('Failed to load the Trace Detail.',{position: 'bottom'}); $toast.default('Failed to load the Trace Detail.',{position: 'bottom'});
}
}; };
}, },
/** /**

View File

@@ -47,7 +47,7 @@
<!-- Sidebar Model --> <!-- Sidebar Model -->
<SidebarView v-model:visible="sidebarView" @switch-map-type="switchMapType" @switch-curve-styles="switchCurveStyles" @switch-rank="switchRank" @switch-data-layer-type="switchDataLayerType" ></SidebarView> <SidebarView v-model:visible="sidebarView" @switch-map-type="switchMapType" @switch-curve-styles="switchCurveStyles" @switch-rank="switchRank" @switch-data-layer-type="switchDataLayerType" ></SidebarView>
<SidebarState v-model:visible="sidebarState" :insights="insights" :stats="stats"></SidebarState> <SidebarState v-model:visible="sidebarState" :insights="insights" :stats="stats"></SidebarState>
<SidebarTraces v-model:visible="sidebarTraces" :traces="traces" :cases="cases" :traceTaskSeq="traceTaskSeq" @switch-Trace-Id="switchTraceId" ref="tracesView"></SidebarTraces> <SidebarTraces v-model:visible="sidebarTraces" @switch-Trace-Id="switchTraceId" ref="tracesView"></SidebarTraces>
<SidebarFilter v-model:visible="sidebarFilter" :filterTasks="filterTasks" :filterStartToEnd="filterStartToEnd" :filterEndToStart="filterEndToStart" :filterTimeframe="filterTimeframe" :filterTrace="filterTrace" <SidebarFilter v-model:visible="sidebarFilter" :filterTasks="filterTasks" :filterStartToEnd="filterStartToEnd" :filterEndToStart="filterEndToStart" :filterTimeframe="filterTimeframe" :filterTrace="filterTrace"
@submit-all="createCy(mapType)" @switch-Trace-Id="switchTraceId" ref="sidevarFilterRef"></SidebarFilter> @submit-all="createCy(mapType)" @switch-Trace-Id="switchTraceId" ref="sidevarFilterRef"></SidebarFilter>
@@ -68,9 +68,9 @@ export default {
const loadingStore = LoadingStore(); const loadingStore = LoadingStore();
const allMapDataStore = AllMapDataStore(); const allMapDataStore = AllMapDataStore();
const { isLoading } = storeToRefs(loadingStore); const { isLoading } = storeToRefs(loadingStore);
const { processMap, bpmn, stats, insights, traceId, traces, traceTaskSeq, cases, filterTasks, filterStartToEnd, filterEndToStart, filterTimeframe, filterTrace, temporaryData, isRuleData, ruleData, logId, createFilterId } = storeToRefs(allMapDataStore); const { processMap, bpmn, stats, insights, traceId, traces, filterTasks, filterStartToEnd, filterEndToStart, filterTimeframe, filterTrace, temporaryData, isRuleData, ruleData, logId, createFilterId } = storeToRefs(allMapDataStore);
return { isLoading, processMap, bpmn, stats, insights, traceId, traces, traceTaskSeq, cases, filterTasks, filterStartToEnd, filterEndToStart, filterTimeframe, filterTrace, logId, createFilterId, temporaryData, isRuleData, ruleData, allMapDataStore} return { isLoading, processMap, bpmn, stats, insights, traceId, traces, filterTasks, filterStartToEnd, filterEndToStart, filterTimeframe, filterTrace, logId, createFilterId, temporaryData, isRuleData, ruleData, allMapDataStore}
}, },
components: { components: {
SidebarView, SidebarView,
@@ -102,6 +102,7 @@ export default {
sidebarState: false, // SideBar: Summary & Insight sidebarState: false, // SideBar: Summary & Insight
sidebarTraces: false, // SideBar: Traces sidebarTraces: false, // SideBar: Traces
sidebarFilter: false, // SideBar: Filter sidebarFilter: false, // SideBar: Filter
infiniteFirstCases: null,
} }
}, },
computed:{ computed:{
@@ -311,7 +312,6 @@ export default {
await this.allMapDataStore.getAllMapData(); await this.allMapDataStore.getAllMapData();
await this.allMapDataStore.getAllTrace(); await this.allMapDataStore.getAllTrace();
this.traceId = await this.traces[0]?.id; // log、filter 檔切換過程中, trace id 不同,將初始 trace id 設定為該檔案的 trace 幣一筆資料的 id。 this.traceId = await this.traces[0]?.id; // log、filter 檔切換過程中, trace id 不同,將初始 trace id 設定為該檔案的 trace 幣一筆資料的 id。
await this.allMapDataStore.getTraceDetail();
this.createCy(this.mapType); this.createCy(this.mapType);
await this.allMapDataStore.getFilterParams(); await this.allMapDataStore.getFilterParams();