Discover: sidebarFilter layout done

This commit is contained in:
chiayin
2023-04-12 16:44:47 +08:00
parent 98f37249bc
commit daed99f84f
12 changed files with 733 additions and 55 deletions

View File

@@ -0,0 +1,347 @@
<template>
<Sidebar :visible="sidebarFilter" :closeIcon="'pi pi-chevron-left'" :modal="false" position="left" :dismissable="true" class="!w-11/12 !bg-neutral-100">
<template #header>
<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'">Filter</li>
<li class="h1 border-r-2 border-neutral-300 pr-4 cursor-pointer hover:text-neutral-900 hover:duration-700" @click="switchTab('funnel')" :class="tab === 'funnel'? 'text-neutral-900': 'text-neutral-500'">Funnel</li>
</ul>
</template>
<!-- header: filter -->
<div v-if="tab === 'filter'" class="pt-4 bg-neutral-100 flex w-full h-full">
<!-- title: filter silect -->
<div class="space-y-2 mr-4 w-56">
<div>
<p class="h2">Filter Type</p>
<div v-for="(item, index) in selectFilter['Filter Type']" :key="index" class="flex align-items-center">
<RadioButton v-model="selectValue[0]" :inputId="item + index" name="Filter Type" :value="item" />
<label :for="item + index" class="ml-2">{{ item }}</label>
</div>
</div>
<div v-show="selectValue[0] === 'Sequence'">
<p class="h2">Activity Sequence</p>
<div v-for="(item, index) in selectFilter['Activity Sequence']" :key="index" class="flex align-items-center">
<RadioButton v-model="selectValue[1]" :inputId="item + index" name="Activity Sequence" :value="item" />
<label :for="item + index" class="ml-2">{{ item }}</label>
</div>
</div>
<div v-show="selectValue[1] === 'Start activity & end activity'">
<p class="h2">Start & End</p>
<div v-for="(item, index) in selectFilter['Start & End']" :key="index" class="flex align-items-center">
<RadioButton v-model="selectValue[2]" :inputId="item + index" name="Start & End" :value="item" />
<label :for="item + index" class="ml-2">{{ item }}</label>
</div>
</div>
<div v-show="selectValue[0] === 'Sequence' && selectValue[1] === 'Sequence'">
<p class="h2">Mode</p>
<div v-for="(item, index) in selectFilter['Mode']" :key="index" class="flex align-items-center">
<RadioButton v-model="selectValue[3]" :inputId="item + index" name="Mode" :value="item" />
<label :for="item + index" class="ml-2">{{ item }}</label>
</div>
</div>
<div v-show="selectValue[0] === 'Attributes'">
<p class="h2">Mode</p>
<div v-for="(item, index) in selectFilter['ModeAtt']" :key="index" class="flex align-items-center">
<RadioButton v-model="selectValue[4]" :inputId="item + index" name="ModeAtt" :value="item" />
<label :for="item + index" class="ml-2">{{ item }}</label>
</div>
</div>
<div>
<p class="h2">Refine</p>
<div v-for="(item, index) in selectFilter['Refine']" :key="index" class="flex align-items-center">
<RadioButton v-model="selectValue[5]" :inputId="item + index" name="Refinee" :value="item" />
<label :for="item + index" class="ml-2">{{ item }}</label>
</div>
</div>
<div v-show="selectValue[0] === 'Timeframes'">
<p class="h2">Containment</p>
<div v-for="(item, index) in selectFilter['Containment']" :key="index" class="flex align-items-center">
<RadioButton v-model="selectValue[6]" :inputId="item + index" name="Containment" :value="item" />
<label :for="item + index" class="ml-2">{{ item }}</label>
</div>
</div>
</div>
<!-- title: Activity Select -->
<div class="space-y-2 w-[calc(100%_-_240px)] h-[calc(100%_-_106px)]">
<p class="h2 ml-1">Activity Select</p>
<!-- Filter task Data-->
<ActOccCase v-if="selectValue[0] === 'Sequence' && selectValue[1] === 'Have activity(s)'" :tableTitle="'Activity List'" :tableData="filterTaskData" :tableSelect="selectFilterTask" :progressWidth ="progressWidth" @on-row-select="onRowAct"></ActOccCase>
<!-- Filter Start Data -->
<ActOcc v-if="selectValue[0] === 'Sequence' && selectValue[1] === 'Start activity & end activity' && selectValue[2] === 'Start'" :tableTitle="'Start activity'" :tableData="filterStartData" :tableSelect="selectFilterStart" :progressWidth ="progressWidth" @on-row-select="onRowStart"></ActOcc>
<!-- Filter End Data -->
<ActOcc v-if="selectValue[0] === 'Sequence' && selectValue[1] === 'Start activity & end activity' && selectValue[2] === 'End'" :tableTitle="'End activity'" :tableData="filterEndData" :tableSelect="selectFilterEnd" :progressWidth ="progressWidth" @on-row-select="onRowEnd"></ActOcc>
<!-- Filter Start And End Data -->
<div v-if="selectValue[0] === 'Sequence' && selectValue[1] === 'Start activity & end activity' && selectValue[2] === 'Start & End'" class="flex justify-between items-center w-full h-full space-x-4 ">
<ActOcc :tableTitle="'Start activity'" :tableData="filterStartToEndData" :tableSelect="selectFilterStartToEnd" :progressWidth ="progressWidth" class="w-1/2" @on-row-select="startRow"></ActOcc>
<ActOcc :tableTitle="'End activity'" :tableData="filterEndToStartData" :tableSelect="selectFilterEndToStart" :progressWidth ="progressWidth" class="w-1/2" @on-row-select="endRow"></ActOcc>
</div>
<!-- Filter Sequence -->
<div v-if="selectValue[0] === 'Sequence' && selectValue[1] === 'Sequence'" class="flex justify-between items-center w-full h-full space-x-4">
<ActAndSeq :filterTaskData="filterTaskData" :progressWidth ="progressWidth" :listSeq="listSeq" @update:listSeq="onUpdateListSeq"></ActAndSeq>
</div>
<!-- Button -->
<div class="float-right space-x-4 px-4 py-2">
<button class="btn btn-sm btn-neutral" @click="reset">Reset</button>
<button class="btn btn-sm btn-neutral">Apply</button>
</div>
</div>
</div>
<!-- header: funnel -->
<div v-if="tab === 'funnel'"></div>
</Sidebar>
</template>
<script>
import ActOccCase from '@/components/Discover/table/actOccCase.vue';
import ActOcc from '@/components/Discover/table/actOcc.vue';
import ActAndSeq from '@/components/Discover/table/actAndSeq.vue';
export default {
props: {
sidebarFilter: {
type: Boolean,
require: true,
},
filterTasks: {
type: Array,
require: true,
},
filterStartToEnd: {
type: Array,
require: true,
},
filterEndToStart: {
type: Array,
require: true,
},
filterTimeframe: {
type: Object,
require: true,
},
filterTrace: {
type: Array,
require: true,
},
},
data() {
return {
selectFilter: {
'Filter Type': ['Sequence', 'Attributes', 'Trace', 'Timeframes'],
'Activity Sequence':['Have activity(s)', 'Start activity & end activity', 'Sequence'],
'Start & End': ['Start', 'End', 'Start & End'],
'Mode': ['Directly follows', 'Eventually follows'],
'ModeAtt': ['Case', 'Activity'],
'Refine': ['Include', 'Exclude'],
'Containment': ['Contained in', 'Started in', 'End in', 'Activity in', 'Trim'],
},
tab: 'filter',
selectValue: ['Sequence', 'Have activity(s)', 'Start', 'Directly follows', 'Case', 'Include', 'Contained in'],
selectFilterTask: null,
selectFilterStart: null,
selectFilterEnd: null,
selectFilterStartToEnd: null,
selectFilterEndToStart: null,
listSeq: [],
//若第一次選擇 start, 則 end 連動改變,若第一次選擇 end, 則 start 連動改變
isStartSelected: null,
isEndSelected: null,
rowData: [],
}
},
components: {
ActOccCase,
ActOcc,
ActAndSeq,
},
computed: {
// All Task
filterTaskData: function() {
let list = [];
this.filterTasks.forEach((task, index) => {
let data = {
label: task.label,
occ_value: Number(task.occurrence_ratio * 100),
occurrences: Number(task.occurrences).toLocaleString('en-US'),
occurrence_ratio: this.getPercentLabel(task.occurrence_ratio),
case_value: Number(task.case_ratio * 100),
cases: task.cases.toLocaleString('en-US'),
case_ratio: this.getPercentLabel(task.case_ratio),
};
list.push(data);
});
list.sort((x, y) => y.occurrences - x.occurrences);
return list;
},
// Start and End Task
filterStartData: function() {
return this.setActData(this.filterStartToEnd);
},
filterEndData: function() {
return this.setActData(this.filterEndToStart);
},
filterStartToEndData: function() {
return this.isEndSelected ? this.setStartAndEndData(this.filterEndToStart, this.rowData, 'sources') : this.setActData(this.filterStartToEnd);
},
filterEndToStartData: function() {
return this.isStartSelected ? this.setStartAndEndData(this.filterStartToEnd, this.rowData, 'sinks') : this.setActData(this.filterEndToStart);
}
},
methods: {
/**
* @param {string} switch Summary or Insight
*/
switchTab(tab) {
this.tab = tab;
},
/**
* Number to percentage
* @param {number} val
* @returns {string} 轉換完成的百分比字串
*/
getPercentLabel(val){
return (val * 100 === 100) ? `${val * 100}%` : `${(val * 100).toFixed(1)}%`;
},
/**
* set progress bar width
* @param {number} value
* @returns {string} 樣式的寬度設定
*/
progressWidth(value){
return `width:${value}%;`
},
// 調整 filterStartData / filterEndData / filterStartToEndData / filterEndToStartData 的內容
/**
* @param {array} array filterStartToEnd / filterEndToStart
*/
setActData(array) {
let list = [];
array.forEach((task, index) => {
let data = {
label: task.label,
occ_value: Number(task.occurrence_ratio * 100),
occurrences: Number(task.occurrences).toLocaleString('en-US'),
occurrence_ratio: this.getPercentLabel(task.occurrence_ratio),
};
list.push(data);
});
return list;
},
/**
* @param {array} select select Have activity(s) rows
*/
onRowAct(select){
this.selectFilterTask = select;
},
/**
* @param {object} e select Start rows
*/
onRowStart(e){
this.selectFilterStart = e.data;
},
/**
* @param {object} e select End rows
*/
onRowEnd(e){
this.selectFilterEnd = e.data;
},
/**
* @param {array} e Update List Seq
*/
onUpdateListSeq(listSeq) {
console.log(this.listSeq);
this.listSeq = listSeq;
},
// 在 Start & End 若第一次選擇 start, 則 end 連動改變,若第一次選擇 end, 則 start 連動改變
/**
* @param {object} e object contains selected row's data
*/
startRow(e){
this.selectFilterStartToEnd = e.data;
if(this.isStartSelected === null || this.isStartSelected === true){
this.isStartSelected = true;
this.isEndSelected = false;
this.rowData = e.data;
}
},
endRow(e) {
this.selectFilterEndToStart = e.data;
if(this.isEndSelected === null || this.isEndSelected === true){
this.isEndSelected = true;
this.isStartSelected = false;
this.rowData = e.data;
}
},
// 重新設定連動的 filterStartToEndData / filterEndToStartData 內容
/**
* @param {array} eventData Start or End List
* @param {object} rowData 所選擇的 row's data
* @param {string} event sinks / sources
*/
setStartAndEndData(eventData, rowData, event){
const filterData = event === 'sinks' ? this.filterEndToStart : this.filterStartToEnd;
const relatedItems = eventData
.find(task => task.label === rowData.label)
?.[event]?.filter(item => filterData.some(ele => ele.label === item))
?.map(item => filterData.find(ele => ele.label === item));
if (!relatedItems) return [];
return relatedItems.map(item => ({
label: item.label,
occ_value: Number(item.occurrence_ratio * 100),
occurrences: Number(item.occurrences).toLocaleString('en-US'),
occurrence_ratio: this.getPercentLabel(item.occurrence_ratio),
}));
// 以下是優化前的程式碼:
// let list = [];
// eventData.forEach((task) => {
// if(task.label === rowData.label) {
// task[event].forEach(item => {
// const filterData = event === 'sinks' ? this.filterEndToStart : this.filterStartToEnd;
// const element = filterData.find(ele => ele.label === item);
// if (element !== undefined) {
// const data = {
// label: element.label,
// occ_value: Number(element.occurrence_ratio * 100),
// occurrences: Number(element.occurrences).toLocaleString('en-US'),
// occurrence_ratio: this.getPercentLabel(element.occurrence_ratio),
// };
// list.push(data);
// };
// })
// };
// });
// return list;
},
/**
* 清空選項
*/
reset() {
this.selectFilterTask = null;
this.selectFilterStart = null;
this.selectFilterEnd = null;
this.selectFilterStartToEnd = null;
this.selectFilterEndToStart = null;
this.listSeq = [];
this.isStartSelected = null;
this.isEndSelected = null;
// this.rowData = [];
console.log('reset');
}
},
}
</script>
<style scoped>
#searchFiles::-webkit-search-cancel-button{
appearance: none;
}
</style>

View File

@@ -19,7 +19,7 @@
</tr>
</thead>
<tbody>
<tr v-for="(trace, key) in traceList" :key="key" class=" cursor-pointer hover:text-primary" @dblclick="switchCaseData(trace.id)">
<tr v-for="(trace, key) in traceList" :key="key" class=" cursor-pointer hover:text-primary" @click="switchCaseData(trace.id)">
<td class="text-xs p-2">#{{ trace.id }}</td>
<td class="text-xs p-2 w-24">
<div class="h-4 w-full bg-neutral-300 rounded-sm overflow-hidden">
@@ -37,7 +37,7 @@
<section class="pl-4 h-full w-[calc(100%_-_320px)]">
<p class="h2 mb-2">Trace #{{ showTraceId }}</p>
<div class="h-52 w-full px-2 mb-2 border border-neutral-300 rounded">
<div class="h-full w-full scrollbar overflow-x-auto">
<div class="h-full w-full">
<div id="cyTrace" ref="cyTrace" class="h-full min-w-full relative"></div>
</div>
</div>

View File

@@ -0,0 +1,86 @@
<template>
<!-- Activity List -->
<div class="bg-neutral-10 border border-neutral-300 rounded-xl px-4 w-full h-full">
<div class="flex justify-between items-center my-2 flex-wrap">
<p class="h2">Activity List&nbsp({{ data.length }})</p>
</div>
<!-- Table -->
<div class="overflow-y-auto overflow-x-auto scrollbar -mx-2 h-[calc(100%_-_64px)]">
<table class="border-separate border-spacing-x-2 table-auto min-w-full" :class="data.length === 0? 'h-full': null">
<thead class="sticky top-0 left-0 z-10 bg-neutral-10">
<tr>
<th class="text-start text-base font-semibold leading-10 px-2 border-b border-neutral-500">Activity</th>
<th class="text-base font-semibold leading-10 px-2 border-b border-neutral-500 text-start" colspan="3">Occurrences</th>
</tr>
</thead>
<Draggable :list="data" group="people" itemKey="name" tag="tbody" animation="300" @end="onEnd">
<template #item="{ element, index }">
<tr>
<td class="px-4 py-2">{{ element.label }}</td>
<td class="px-4 py-2 w-24">
<div class="h-4 min-w-[96px] bg-neutral-300 rounded-sm overflow-hidden">
<div class="h-full bg-primary" :style="progressWidth(element.occ_value)"></div>
</div>
</td>
<td class="px-4 py-2 text-right">{{ element.occurrences }}</td>
<td class="px-4 py-2 text-right">{{ element.occurrence_ratio }}</td>
</tr>
</template>
</Draggable>
</table>
</div>
</div>
<!-- Sequence -->
<div class="bg-neutral-10 border border-neutral-300 rounded-xl px-4 pb-4 w-full h-full relative">
<p class="h2 border-b border-500 my-2">Sequence&nbsp({{ listSeq.length }})</p>
<!-- No Data -->
<div v-if="listSequence.length === 0" class="p-4 w-[calc(100%_-_32px)] h-5/6 flex justify-center items-center absolute">
<p class="text-neutral-500">Please drag and drop activity(s) here and sort.</p>
</div>
<!-- Have Data -->
<div class="py-4 m-auto w-full h-[calc(100%_-_56px)]">
<div class="w-full h-full overflow-y-auto overflow-x-auto scrollbar px-4 text-center">
<draggable class="h-full" :list="listSequence" group="people" itemKey="name" animation="300" @end="onEnd">
<template #item="{ element, index }">
<div>
<div class="w-full p-2 border border-primary rounded text-primary">
<span>{{ element.label }}</span>
</div>
<span v-show="index !== listSeq.length - 1" class="pi pi-chevron-down !text-lg inline-block py-2 "></span>
</div>
</template>
</draggable>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
filterTaskData: {
type: Array,
required: true,
},
progressWidth: {
type: Function,
required: false,
},
listSeq: {
type: Array,
required: true,
}
},
data() {
return {
listSequence: this.listSeq,
data: this.filterTaskData,
}
},
methods: {
onEnd() {
this.$emit('update:listSeq', this.listSequence);
}
}
}
</script>

View File

@@ -0,0 +1,71 @@
<template>
<div class="bg-neutral-10 border border-neutral-300 rounded-xl px-4 w-full h-full">
<div class="flex justify-between items-center my-2 flex-wrap">
<p class="h2">{{ tableTitle }}&nbsp({{ tableData.length }})</p>
<!-- Search -->
<!-- <Search></Search> -->
</div>
<!-- Table -->
<div class="overflow-y-auto overflow-x-auto scrollbar -mx-2 h-[calc(100%_-_64px)]">
<DataTable v-model:selection="select" :value="tableData" dataKey="label" tableClass="w-full !border-separate !border-spacing-x-2 !table-auto" @row-select="onRowSelect">
<ColumnGroup type="header">
<Row>
<Column selectionMode="single" headerClass="w-8 !p-2 !bg-neutral-10 !border-neutral-500 sticky top-0 left-0 z-10 bg-neutral-10"></Column>
<Column field="label" header="Activity" headerClass="!bg-neutral-10 !border-neutral-500 !py-2 sticky top-0 left-0 z-10 bg-neutral-10" sortable />
<Column field="occ_value" header="Occurrences" headerClass="!bg-neutral-10 !border-neutral-500 !py-2 sticky top-0 left-0 z-10 bg-neutral-10" sortable :colspan="3" />
</Row>
</ColumnGroup>
<Column selectionMode="single" bodyClass="!p-2 !border-0"></Column>
<Column field="label" header="Activity" bodyClass="break-words !py-2 !border-0"></Column>
<Column header="進度條" bodyClass="!py-2 !border-0 min-w-[96px]">
<template #body="slotProps">
<div class="h-4 w-full bg-neutral-300 rounded-sm overflow-hidden">
<div class="h-full bg-primary" :style="progressWidth(slotProps.data.occ_value)"></div>
</div>
</template>
</Column>
<Column field="occurrences" header="Occurrences" bodyClass="!text-right !py-2 !border-0"></Column>
<Column field="occurrence_ratio" header="Occurrence Ratio" bodyClass="!text-right !py-2 !border-0"></Column>
</DataTable>
</div>
</div>
</template>
<script>
import Search from '@/components/Search.vue';
export default {
props: {
tableTitle: {
type: String,
required: true,
},
tableData: {
type: Array,
required: true,
},
tableSelect: {
type: [Object, Array],
default: null
},
progressWidth: {
type: Function,
required: false,
}
},
data() {
return {
select: this.tableSelect,
metaKey: true
}
},
components: {
Search,
},
methods: {
onRowSelect(e) {
this.$emit('on-row-select', e)
}
},
}
</script>

View File

@@ -0,0 +1,92 @@
<template>
<div class="bg-neutral-10 border border-neutral-300 rounded-xl px-4 h-full">
<div class="flex justify-between items-center my-2">
<p class="h2">{{ tableTitle }}&nbsp({{ data.length }})</p>
<!-- Search -->
<!-- <Search></Search> -->
</div>
<!-- Table -->
<div class="overflow-y-auto overflow-x-auto scrollbar -mx-2 h-[calc(100%_-_64px)]">
<DataTable v-model:selection="select" :value="data" tableClass="w-full !border-separate !border-spacing-x-2 !table-auto" @row-select="onRowSelect" @row-unselect="onRowUnselect" @row-select-all="onRowSelectAll" @row-unselect-all="onRowUnelectAll">
<ColumnGroup type="header">
<Row>
<Column selectionMode="multiple" headerClass="w-8 !p-2 !bg-neutral-10 !border-neutral-500 sticky top-0 left-0 z-10 bg-neutral-10"></Column>
<Column field="label" header="Activity" headerClass="!bg-neutral-10 !border-neutral-500 !py-2 sticky top-0 left-0 z-10 bg-neutral-10" sortable />
<Column field="occ_value" header="Occurrences" headerClass="!bg-neutral-10 !border-neutral-500 !py-2 sticky top-0 left-0 z-10 bg-neutral-10" sortable :colspan="3" />
<Column field="case_value" headerClass="!bg-neutral-10 !border-neutral-500 !py-2 sticky top-0 left-0 z-10 bg-neutral-10" header="Cases with Activity" sortable :colspan="3" />
</Row>
</ColumnGroup>
<Column selectionMode="multiple" bodyClass="!p-2 !border-0"></Column>
<Column field="label" header="Activity" bodyClass="break-words !py-2 !border-0"></Column>
<Column header="進度條" bodyClass="!py-2 !border-0 min-w-[96px]">
<template #body="slotProps">
<div class="h-4 w-full bg-neutral-300 rounded-sm overflow-hidden">
<div class="h-full bg-primary" :style="progressWidth(slotProps.data.occ_value)"></div>
</div>
</template>
</Column>
<Column field="occurrences" header="Occurrences" bodyClass="!text-right !py-2 !border-0"></Column>
<Column field="occurrence_ratio" header="O2" bodyClass="!text-right !py-2 !border-0"></Column>
<Column header="進度條" bodyClass="!py-2 !border-0 min-w-[96px]">
<template #body="slotProps">
<div class="h-4 w-full bg-neutral-300 rounded-sm overflow-hidden">
<div class="h-full bg-primary" :style="progressWidth(slotProps.data.case_value)"></div>
</div>
</template>
</Column>
<Column field="cases" header="Cases with Activity" bodyClass="!text-right !py-2 !border-0"></Column>
<Column field="case_ratio" header="C2" bodyClass="!text-right !py-2 !border-0"></Column>
</DataTable>
</div>
</div>
</template>
<script>
import Search from '@/components/Search.vue';
export default {
props: {
tableTitle: {
type: String,
require: true,
},
tableData: {
type: Array,
require: true,
},
tableSelect: {
type: Array,
require: true,
},
progressWidth: {
type: Function,
require: false,
}
},
data() {
return {
select: this.tableSelect,
data: this.tableData
}
},
components: {
Search,
},
methods: {
onRowSelect() {
this.$emit('on-row-select', this.select);
},
onRowUnselect() {
this.$emit('on-row-select', this.select);
},
onRowSelectAll(e) {
this.select = e.data;
this.$emit('on-row-select', this.select);
},
onRowUnelectAll(e) {
this.select = null;
this.$emit('on-row-select', this.select)
}
}
}
</script>

View File

@@ -22,7 +22,7 @@
<span class="absolute top-2 bottom-1.5 right-0.5 flex justify-center items-center gap-2">
<IconSetting class="w-6 h-6 cursor-pointer"></IconSetting>
<span class="w-px h-6 block after:border after:border-neutral-300 after:content-['']"></span>
<button class="pr-2 py-1 rounded-r-full">
<button class="pr-2">
<IconSearch class="w-6 h-6"></IconSearch>
</button>
</span>
@@ -44,7 +44,7 @@
</template>
<script>
import filesStore from '@/stores/files.js';
import filesStore from '@/stores/files.js';
import IconSearch from '@/components/icons/IconSearch.vue';
import IconSetting from '@/components/icons/IconSetting.vue';

26
src/components/Search.vue Normal file
View File

@@ -0,0 +1,26 @@
<template>
<form role="search">
<label for="searchFiles" class="mr-4 relative">
<input type="search" id="searchFiles" placeholder="Search Activity" class="px-5 py-2 w-52 rounded-full text-sm align-middle duration-300 border bg-neutral-100 border-neutral-300 hover:border-neutral-500 focus:outline-none focus:ring focus:border-neutral-500">
<span class="absolute top-2 bottom-0.5 right-0.5 flex justify-center items-center gap-2">
<IconSetting class="w-6 h-6 cursor-pointer"></IconSetting>
<span class="w-px h-6 block after:border after:border-neutral-300 after:content-['']"></span>
<button class="pr-2">
<IconSearch class="w-6 h-6"></IconSearch>
</button>
</span>
</label>
</form>
</template>
<script>
import IconSearch from '@/components/icons/IconSearch.vue';
import IconSetting from '@/components/icons/IconSetting.vue';
export default {
components: {
IconSearch,
IconSetting
}
}
</script>

View File

@@ -11,6 +11,7 @@ import ToastPlugin from 'vue-toast-notification';
import cytoscape from 'cytoscape';
import dagre from 'cytoscape-dagre';
import popper from 'cytoscape-popper';
import draggable from 'vuedraggable';
// import CSS
import "./assets/main.css";
@@ -31,7 +32,9 @@ import TabPanel from 'primevue/tabpanel';
import DataTable from 'primevue/datatable';
import Column from 'primevue/column';
import ColumnGroup from 'primevue/columngroup'; // optional
import Row from 'primevue/row';
import Row from 'primevue/row'; // optional
import RadioButton from 'primevue/radiobutton';
import PickList from 'primevue/picklist';
const emitter = mitt();
const app = createApp(App);
@@ -71,5 +74,8 @@ app.component('DataTable', DataTable);
app.component('Column', Column);
app.component('ColumnGroup', ColumnGroup);
app.component('Row', Row);
app.component('RadioButton', RadioButton);
app.component('PickList', PickList);
app.component('Draggable', draggable); // 拖曳
app.mount("#app");

View File

@@ -21,6 +21,11 @@ export default defineStore('allMapDataStore', {
allTrace: [],
allCase: [],
allTraceTaskSeq: [],
allFilterTask: [],
allFilterStartToEnd: [],
allFilterEndToStart: [],
allFilterTimeframe: {},
allFilterTrace: [],
httpStatus: 200,
}),
getters: {
@@ -44,7 +49,25 @@ export default defineStore('allMapDataStore', {
},
traceTaskSeq: state => {
return state.allTraceTaskSeq;
}
},
// All tasks
filterTasks: state => {
return state.allFilterTask;
},
// form start to end tasks
filterStartToEnd: state => {
return state.allFilterStartToEnd;
},
// form end to start tasks
filterEndToStart: state => {
return state.allFilterEndToStart;
},
filterTimeframe: state => {
return state.allFilterTimeframe;
},
filterTrace: state => {
return state.allFilterTrace;
},
},
actions: {
/**
@@ -80,12 +103,10 @@ export default defineStore('allMapDataStore', {
*/
async getAllTrace() {
let logId = this.logId;
// const api = `/api/filters/params?log_id=${logId}`;
const api = `/api/logs/${logId}/traces`;
try {
const response = await this.$axios.get(api);
console.log(response);
this.allTrace = response.data;
if(this.httpStatus < 300) loading.isLoading = false;
@@ -134,5 +155,34 @@ export default defineStore('allMapDataStore', {
});
};
},
/**
* fetch Filter Parameters api.
*/
async getFilterParams() {
let logId = this.logId;
const api = `/api/filters/params?log_id=${logId}`;
try {
const response = await this.$axios.get(api);
this.allFilterTask = response.data.tasks;
this.allFilterStartToEnd = response.data.sources;
this.allFilterEndToStart = response.data.sinks;
this.allFilterTimeframe = response.data.timeframe;
this.allFilterTrace = response.data.trace;
if(this.httpStatus < 300) loading.isLoading = false;
} catch(error) {
this.httpStatus = error.request.status;
delay().then(() =>{
loading.isLoading = true;
return delay(1000);
}).then(()=>{
loading.isLoading = false;
return delay(500);
}).then(() => {
$toast.default('Failed to load the Filter Parameters.');
});
};
},
},
})

View File

@@ -1,18 +1,18 @@
<template>
<!-- Sidebar: Switch data type -->
<div class="flex flex-col justify-between py-4 w-14 h-screen-main absolute bottom-0 left-0 z-10" :class="sidebarLeftValue? 'bg-neutral-100':''">
<div class="flex flex-col justify-between py-4 w-14 h-screen-main absolute bottom-0 left-0 z-10" :class="sidebarLeftValue? 'bg-neutral-50':''">
<ul class="space-y-4 flex flex-col justify-center items-center">
<li class="inline-flex items-center justify-center border border-neutral-500 rounded-full w-9 h-9 cursor-pointer bg-neutral-100 drop-shadow hover:border-primary" @click="sidebarView = true" :class="{'border-primary': sidebarView}">
<li class="inline-flex items-center justify-center border border-neutral-500 rounded-full w-9 h-9 cursor-pointer bg-neutral-50 drop-shadow hover:border-primary" @click="sidebarView = true" :class="{'border-primary': sidebarView}">
<span class="material-symbols-outlined text-2xl hover:text-primary p-1.5" :class="[sidebarView ? 'text-primary' : 'text-neutral-500']">
track_changes
</span>
</li>
<li class="inline-flex items-center justify-center border border-neutral-500 rounded-full w-9 h-9 cursor-pointer bg-neutral-100 drop-shadow hover:border-primary" @click="sidebarFilter = true" :class="{'border-primary': sidebarFilter}">
<li class="inline-flex items-center justify-center border border-neutral-500 rounded-full w-9 h-9 cursor-pointer bg-neutral-50 drop-shadow hover:border-primary" @click="sidebarFilter = true" :class="{'border-primary': sidebarFilter}">
<span class="material-symbols-outlined text-2xl hover:text-primary p-1.5" :class="[sidebarFilter ? 'text-primary' : 'text-neutral-500']">
tornado
</span>
</li>
<li class="inline-flex items-center justify-center border border-neutral-500 rounded-full w-9 h-9 cursor-pointer bg-neutral-100 drop-shadow hover:border-primary" @click="sidebarTraces = true" :class="{'border-primary': sidebarTraces}">
<li class="inline-flex items-center justify-center border border-neutral-500 rounded-full w-9 h-9 cursor-pointer bg-neutral-50 drop-shadow hover:border-primary" @click="sidebarTraces = true" :class="{'border-primary': sidebarTraces}">
<span class="material-symbols-outlined text-2xl hover:text-primary p-1.5" :class="[sidebarTraces ? 'text-primary' : 'text-neutral-500']">
rebase
</span>
@@ -20,7 +20,7 @@
</ul>
<ul class="flex flex-col justify-center items-center">
<li class="inline-flex items-center justify-center border border-neutral-500 rounded-full w-9 h-9 cursor-pointer bg-neutral-100 drop-shadow hover:border-primary">
<li class="inline-flex items-center justify-center border border-neutral-500 rounded-full w-9 h-9 cursor-pointer bg-neutral-50 drop-shadow hover:border-primary">
<span class="material-symbols-outlined text-2xl text-neutral-500 hover:text-primary p-1.5">
highlight
</span>
@@ -29,14 +29,14 @@
</div>
<!-- Cytoscape Map -->
<div class="min-w-full h-screen-main bg-neutral-100 -z-40">
<div class="min-w-full h-screen-main bg-neutral-50 -z-40">
<div id="cy" class="min-w-full h-screen-main"></div>
</div>
<!-- Sidebar: State -->
<div class="bg-transparent py-4 w-14 h-screen-main z-10 bottom-0 right-0 absolute">
<ul class="flex flex-col justify-center items-center">
<li class="inline-flex items-center justify-center border border-neutral-500 rounded-full w-9 h-9 cursor-pointer bg-neutral-100 drop-shadow hover:border-primary" @click="sidebarState = true" :class="{'border-primary': sidebarState}">
<li class="inline-flex items-center justify-center border border-neutral-500 rounded-full w-9 h-9 cursor-pointer bg-neutral-50 drop-shadow hover:border-primary" @click="sidebarState = true" :class="{'border-primary': sidebarState}">
<span class="material-symbols-outlined text-2xl text-neutral-500 hover:text-primary p-1.5" :class="[sidebarState ? 'text-primary' : 'text-neutral-500']">
info
</span>
@@ -48,34 +48,8 @@
<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>
<SidebarTraces v-model:visible="sidebarTraces" :traces="traces" :cases="cases" :traceTaskSeq="traceTaskSeq" @switch-Trace-Id="switchTraceId" ref="tracesView"></SidebarTraces>
<SidebarFilter v-model:visible="sidebarFilter" :filterTasks="filterTasks" :filterStartToEnd="filterStartToEnd" :filterEndToStart="filterEndToStart" :filterTimeframe="filterTimeframe" :filterTrace="filterTrace"></SidebarFilter>
<Sidebar v-model:visible="sidebarFilter" :closeIcon="'pi pi-chevron-left'" :modal="false" position="left" :dismissable="true" class="!w-11/12">
<template #header>
<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': ''">Filter</li>
<li class="h1 border-r-2 border-neutral-300 pr-4 cursor-pointer hover:text-neutral-900 hover:duration-700" @click="switchTab('funnel')" :class="tab === 'funnel'? 'text-neutral-900': ''">Funnel</li>
</ul>
</template>
<!-- header: filter -->
<div v-if="tab === 'filter'" class="pt-4 bg-neutral-100">
<!-- filter silect -->
<div>
<div>
<p class="h2">Filter Type</p>
</div>
</div>
</div>
<!-- header: funnel -->
<div v-if="tab === 'funnel'"></div>
</Sidebar>
</template>
<script>
@@ -86,21 +60,22 @@ import cytoscapeMap from '@/module/cytoscapeMap.js';
import SidebarView from '@/components/Discover/sidebarView.vue';
import SidebarState from '@/components/Discover/sidebarState.vue';
import SidebarTraces from '@/components/Discover/sidebarTraces.vue';
import SidebarFilter from '@/components/Discover/sidebarFilter.vue';
export default {
setup() {
const loadingStore = LoadingStore();
const allMapDataStore = AllMapDataStore();
const { isLoading } = storeToRefs(loadingStore);
const { processMap, bpmn, stats, insights, traces, traceTaskSeq,cases } = storeToRefs(allMapDataStore);
const { processMap, bpmn, stats, insights, traces, traceTaskSeq, cases, filterTasks, filterStartToEnd, filterEndToStart, filterTimeframe, filterTrace, } = storeToRefs(allMapDataStore);
return { isLoading, processMap, bpmn, stats, insights, traces, traceTaskSeq,cases, allMapDataStore,}
return { isLoading, processMap, bpmn, stats, insights, traces, traceTaskSeq, cases, filterTasks, filterStartToEnd, filterEndToStart, filterTimeframe, filterTrace, allMapDataStore}
},
components: {
SidebarView,
SidebarState,
SidebarTraces,
// SidebarFilter,
SidebarFilter,
},
data() {
return {
@@ -126,8 +101,6 @@ export default {
sidebarState: false, // SideBar: Summary & Insight
sidebarTraces: false, // SideBar: Traces
sidebarFilter: false, // SideBar: Filter
tab: 'filter',
}
},
computed:{
@@ -137,12 +110,6 @@ export default {
}
},
methods: {
/**
* @param {string} switch Summary or Insight
*/
switchTab(tab) {
this.tab = tab;
},
/**
* switch map type
* @param {string} type processMap | bpmn
@@ -333,6 +300,8 @@ export default {
await this.allMapDataStore.getTraceDetail();
this.createCy(this.mapType);
this.allMapDataStore.getFilterParams();
},
mounted() {
// this.isLoading = false;