Merge branch 'sonar'

This commit is contained in:
Cindy Chang
2024-08-05 09:32:20 +08:00
39 changed files with 430 additions and 436 deletions

View File

@@ -2,8 +2,6 @@
<RouterView /> <RouterView />
</template> </template>
<style scoped></style>
<script setup lang="ts"> <script setup lang="ts">
import { RouterView } from "vue-router"; import { RouterView } from "vue-router";
</script> </script>

View File

@@ -43,7 +43,7 @@
} }
.p-sidebar-header { .p-sidebar-header {
@apply bg-neutral-200 border-b border-neutral-300 !py-2 !justify-between @apply bg-neutral-200 border-b border-neutral-300 !py-2 !justify-between
}; }
.p-sidebar-right .p-sidebar-header { .p-sidebar-right .p-sidebar-header {
@apply flex-row-reverse !justify-end text-neutral-500 @apply flex-row-reverse !justify-end text-neutral-500
} }

View File

@@ -38,13 +38,6 @@
</p> </p>
<Chart type="bar" :data="casesChartData" :options="casesChartOptions" class="w-[99%]"/> <Chart type="bar" :data="casesChartData" :options="casesChartOptions" class="w-[99%]"/>
</div> </div>
<!-- Fitness 暫時不做 basis-1/3 basis-1/2 -->
<!-- <div class="border rounded border-neutral-300 p-2 bg-neutral-10 basis-1/3">
<p class="h2 pl-2 flex justify-between items-center">
<span>Fitness<span class="material-symbols-outlined text-sm align-middle ml-2">info</span></span>
<span class="text-2xl">{{ data.charts.fitness }}</span>
</p>
</div> -->
</div> </div>
<!-- effect --> <!-- effect -->
<section> <section>
@@ -104,6 +97,16 @@
<div class="border rounded border-neutral-300 p-2 bg-neutral-10 w-full"> <div class="border rounded border-neutral-300 p-2 bg-neutral-10 w-full">
<p class="h2 pl-2 mb-2">Short Loop(s)</p> <p class="h2 pl-2 mb-2">Short Loop(s)</p>
<table class="text-sm min-w-full table-fixed"> <table class="text-sm min-w-full table-fixed">
<caption class="hidden">Loop List</caption>
<thead class="hidden">
<tr>
<th class="w-1/5 px-4 py-2 hidden"></th>
<th class="w-1/5 px-4 py-2 hidden"></th>
<th class="w-1/5 px-4 py-2 hidden"></th>
<th class="w-1/5 px-4 py-2 hidden"></th>
<th class="w-1/5 px-4 py-2 hidden"></th>
</tr>
</thead>
<tbody> <tbody>
<tr v-for="(trace, key) in data.loops" :key="key"> <tr v-for="(trace, key) in data.loops" :key="key">
<td class="p-2 pl-6 truncate max-w-0 w-1/3"> <td class="p-2 pl-6 truncate max-w-0 w-1/3">
@@ -152,6 +155,16 @@
<div v-else class="border rounded border-neutral-300 p-2 bg-neutral-10 " :class="data.timeTrend.chart !== null ? 'w-1/2' : 'w-full'"> <div v-else class="border rounded border-neutral-300 p-2 bg-neutral-10 " :class="data.timeTrend.chart !== null ? 'w-1/2' : 'w-full'">
<p class="h2 pl-2 mb-2">Issue List<span class="material-symbols-outlined !text-sm align-middle ml-2 cursor-pointer" v-tooltip.bottom="tooltip.issueList">info</span></p> <p class="h2 pl-2 mb-2">Issue List<span class="material-symbols-outlined !text-sm align-middle ml-2 cursor-pointer" v-tooltip.bottom="tooltip.issueList">info</span></p>
<table class="text-sm min-w-full table-fixed"> <table class="text-sm min-w-full table-fixed">
<caption class="hidden">Issues List</caption>
<thead class="hidden">
<tr>
<th class="w-1/5 px-4 py-2 hidden"></th>
<th class="w-1/5 px-4 py-2 hidden"></th>
<th class="w-1/5 px-4 py-2 hidden"></th>
<th class="w-1/5 px-4 py-2 hidden"></th>
<th class="w-1/5 px-4 py-2 hidden"></th>
</tr>
</thead>
<tbody> <tbody>
<tr v-for="(trace, key) in data.issues" :key="key"> <tr v-for="(trace, key) in data.issues" :key="key">
<td class="p-2 pl-6 truncate max-w-0 w-1/3"> <td class="p-2 pl-6 truncate max-w-0 w-1/3">
@@ -500,8 +513,9 @@ export default {
color: '#334155', color: '#334155',
align: 'inner', align: 'inner',
callback: function(value, index, values) { callback: function(value, index, values) {
if (value === 0) return `${value * 100}%`; if (value === 0 || value === 1) {
else if (value === 1) return `${value * 100}%`; return `${value * 100}%`;
}
}, },
}, },
grid: { grid: {
@@ -585,8 +599,9 @@ export default {
color: '#334155', color: '#334155',
align: 'inner', align: 'inner',
callback: function(value, index, values) { callback: function(value, index, values) {
if (index === 0) return shortScaleNumber(value); if (index === 0 || index === values.length - 1) {
else if (index === values.length - 1) return shortScaleNumber(value); return shortScaleNumber(value);
}
}, },
}, },
grid: { grid: {
@@ -615,7 +630,7 @@ export default {
let max = yMax * 1.1; let max = yMax * 1.1;
let xVal = timeRange(xMin, xMax, 100); let xVal = timeRange(xMin, xMax, 100);
let yVal = yTimeRange(data, 100, yMin, yMax); let yVal = yTimeRange(data, 100, yMin, yMax);
data = xVal.map((x, index) => ({ x, y: yVal[index] })); xVal.map((x, index) => ({ x, y: yVal[index] }));
let formattedXVal = xVal.map(value => formatTime(value)); let formattedXVal = xVal.map(value => formatTime(value));
formattedXVal = formatMaxTwo(formattedXVal); formattedXVal = formatMaxTwo(formattedXVal);
let selectTimeMinIndex = getXIndex(xVal, this.selectDurationTime.min); let selectTimeMinIndex = getXIndex(xVal, this.selectDurationTime.min);

View File

@@ -55,7 +55,6 @@ export default {
}, },
computed: { computed: {
datadata: function() { datadata: function() {
// TODO Activity List 的 dblclick, drag & drop 要改假刪除
// Activity List 要排序 // Activity List 要排序
let newData; let newData;
if(this.data !== null) { if(this.data !== null) {
@@ -114,14 +113,16 @@ export default {
// 拖曳結束要顯示箭頭,但最後一個不用 // 拖曳結束要顯示箭頭,但最後一個不用
const lastChild = evt.item.lastChild; const lastChild = evt.item.lastChild;
const listIndex = this.listSequence.length - 1 const listIndex = this.listSequence.length - 1
evt.oldIndex !== listIndex ? lastChild.style.display = '' : null; if (evt.oldIndex !== listIndex) {
lastChild.style.display = '';
}
// reset: 拖曳最後一個元素時,倒數第二的元素的箭頭要隱藏 // reset: 拖曳最後一個元素時,倒數第二的元素的箭頭要隱藏
this.lastItemIndex = null; this.lastItemIndex = null;
}, },
}, },
created() { created() {
let newlist = JSON.parse(JSON.stringify(this.listSeq)); let newlist = JSON.parse(JSON.stringify(this.listSeq));
this.isSubmit ? this.listSequence = newlist : this.listSequence = []; this.listSequence = this.isSubmit ? newlist : [];
this.$emitter.on('reset', (data) => { this.$emitter.on('reset', (data) => {
this.listSequence = []; this.listSequence = [];
}); });

View File

@@ -183,115 +183,51 @@ export default {
}); });
this.$emitter.on('actRadioData', (newData) => { this.$emitter.on('actRadioData', (newData) => {
let data = JSON.parse(JSON.stringify(newData)); // 深拷貝原始 cases 的內容 let data = JSON.parse(JSON.stringify(newData)); // 深拷貝原始 cases 的內容
switch (data.category) {
// Activity sequence const categoryMapping = {
case 'cfmSeqStart': 'cfmSeqStart': ['Start', 'selectCfmSeqStart', 'selectCfmSeqEnd'],
if(this.isStartSelected === true && data.task !== this.selectCfmSeqStart) this.selectCfmSeqEnd = null; 'cfmSeqEnd': ['End', 'selectCfmSeqEnd', 'selectCfmSeqStart'],
data.category = 'Start'; 'cfmPtEteStart': ['Start', 'selectCfmPtEteStart'],
this.selectCfmSeqStart = data; 'cfmPtEteEnd': ['End', 'selectCfmPtEteEnd'],
break; 'cfmPtEteSEStart': ['Start', 'selectCfmPtEteSEStart', 'selectCfmPtEteSEEnd'],
case 'cfmSeqEnd': 'cfmPtEteSEEnd': ['End', 'selectCfmPtEteSEEnd', 'selectCfmPtEteSEStart'],
if(this.isEndSelected === true && data.task !== this.selectCfmSeqEnd)this.selectCfmSeqStart = null; 'cfmPtPStart': ['From', 'selectCfmPtPStart'],
data.category = 'End'; 'cfmPtPEnd': ['To', 'selectCfmPtPEnd'],
this.selectCfmSeqEnd = data; 'cfmPtPSEStart': ['From', 'selectCfmPtPSEStart', 'selectCfmPtPSEEnd'],
break; 'cfmPtPSEEnd': ['To', 'selectCfmPtPSEEnd', 'selectCfmPtPSEStart'],
// Processing time 'cfmWtEteStart': ['Start', 'selectCfmWtEteStart'],
case 'cfmPtEteStart': 'cfmWtEteEnd': ['End', 'selectCfmWtEteEnd'],
data.category = 'Start'; 'cfmWtEteSEStart': ['Start', 'selectCfmWtEteSEStart', 'selectCfmWtEteSEEnd'],
this.selectCfmPtEteStart = [data]; 'cfmWtEteSEEnd': ['End', 'selectCfmWtEteSEEnd', 'selectCfmWtEteSEStart'],
break; 'cfmWtPStart': ['From', 'selectCfmWtPStart'],
case 'cfmPtEteEnd': 'cfmWtPEnd': ['To', 'selectCfmWtPEnd'],
data.category = 'End'; 'cfmWtPSEStart': ['From', 'selectCfmWtPSEStart', 'selectCfmWtPSEEnd'],
this.selectCfmPtEteEnd = [data]; 'cfmWtPSEEnd': ['To', 'selectCfmWtPSEEnd', 'selectCfmWtPSEStart'],
break; 'cfmCtEteStart': ['Start', 'selectCfmCtEteStart'],
case 'cfmPtEteSEStart': 'cfmCtEteEnd': ['End', 'selectCfmCtEteEnd'],
if(this.isStartSelected === true && data.task !== this.selectCfmPtEteSEStart) this.selectCfmPtEteSEEnd = null; 'cfmCtEteSEStart': ['Start', 'selectCfmCtEteSEStart', 'selectCfmCtEteSEEnd'],
data.category = 'Start'; 'cfmCtEteSEEnd': ['End', 'selectCfmCtEteSEEnd', 'selectCfmCtEteSEStart']
this.selectCfmPtEteSEStart = data;
break;
case 'cfmPtEteSEEnd':
if(this.isEndSelected === true && data.task !== this.selectCfmPtEteSEEnd)this.selectCfmPtEteSEStart = null;
data.category = 'End';
this.selectCfmPtEteSEEnd = data;
break;
case 'cfmPtPStart':
data.category = 'From';
this.selectCfmPtPStart = [data];
break;
case 'cfmPtPEnd':
data.category = 'To';
this.selectCfmPtPEnd = [data];
break;
case 'cfmPtPSEStart':
if(this.isStartSelected === true && data.task !== this.selectCfmPtPSEStart) this.selectCfmPtPSEEnd = null;
data.category = 'From';
this.selectCfmPtPSEStart = data;
break;
case 'cfmPtPSEEnd':
if(this.isEndSelected === true && data.task !== this.selectCfmPtPSEEnd)this.selectCfmPtPSEStart = null;
data.category = 'To';
this.selectCfmPtPSEEnd = data;
break;
// Waiting time
case 'cfmWtEteStart':
data.category = 'Start';
this.selectCfmWtEteStart = [data];
break;
case 'cfmWtEteEnd':
data.category = 'End';
this.selectCfmWtEteEnd = [data];
break;
case 'cfmWtEteSEStart':
if(this.isStartSelected === true && data.task !== this.selectCfmWtEteSEStart) this.selectCfmWtEteSEEnd = null;
data.category = 'Start';
this.selectCfmWtEteSEStart = data;
break;
case 'cfmWtEteSEEnd':
if(this.isEndSelected === true && data.task !== this.selectCfmWtEteSEEnd)this.selectCfmWtEteSEStart = null;
data.category = 'End';
this.selectCfmWtEteSEEnd = data;
break;
case 'cfmWtPStart':
data.category = 'From';
this.selectCfmWtPStart = [data];
break;
case 'cfmWtPEnd':
data.category = 'To';
this.selectCfmWtPEnd = [data];
break;
case 'cfmWtPSEStart':
if(this.isStartSelected === true && data.task !== this.selectCfmWtPSEStart) this.selectCfmWtPSEEnd = null;
data.category = 'From';
this.selectCfmWtPSEStart = data;
break;
case 'cfmWtPSEEnd':
if(this.isEndSelected === true && data.task !== this.selectCfmWtPSEEnd)this.selectCfmWtPSEStart = null;
data.category = 'To';
this.selectCfmWtPSEEnd = data;
break;
// Cycle time
case 'cfmCtEteStart':
data.category = 'Start';
this.selectCfmCtEteStart = [data];
break;
case 'cfmCtEteEnd':
data.category = 'End';
this.selectCfmCtEteEnd = [data];
break;
case 'cfmCtEteSEStart':
if(this.isStartSelected === true && data.task !== this.selectCfmCtEteSEStart) this.selectCfmCtEteSEEnd = null;
data.category = 'Start';
this.selectCfmCtEteSEStart = data;
break;
case 'cfmCtEteSEEnd':
if(this.isEndSelected === true && data.task !== this.selectCfmCtEteSEEnd)this.selectCfmCtEteSEStart = null;
data.category = 'End';
this.selectCfmCtEteSEEnd = data;
break;
default:
if(this.selectedRuleType === 'Activity duration') this.durationData = [data.task];
break;
}; };
const updateSelection = (key, mainSelector, secondarySelector) => {
if (this[mainSelector]) {
if (data.task !== this[mainSelector]) this[secondarySelector] = null;
}
data.category = categoryMapping[key][0];
this[mainSelector] = data;
};
if (categoryMapping[data.category]) {
const [category, mainSelector, secondarySelector] = categoryMapping[data.category];
if (secondarySelector) {
updateSelection(data.category, mainSelector, secondarySelector);
} else {
data.category = category;
this[mainSelector] = [data];
}
} else if (this.selectedRuleType === 'Activity duration') {
this.durationData = [data.task];
}
}); });
this.$emitter.on('getListSequence', (data) => { this.$emitter.on('getListSequence', (data) => {
switch (data.category) { switch (data.category) {

View File

@@ -288,7 +288,11 @@ export default {
*/ */
setStartAndEndData(data, category, task) { setStartAndEndData(data, category, task) {
let oppositeCategory = ''; let oppositeCategory = '';
category === 'start' ? oppositeCategory = 'end' : oppositeCategory = 'start'; if (category === 'start') {
oppositeCategory = 'end';
} else {
oppositeCategory = 'start';
};
let newData = data.filter(i => i[category] === task).map(i => i[oppositeCategory]); let newData = data.filter(i => i[category] === task).map(i => i[oppositeCategory]);
newData = [...new Set(newData)]; newData = [...new Set(newData)];
return newData; return newData;
@@ -361,8 +365,6 @@ export default {
this.task = this.isSubmitShowDataSeq.task; this.task = this.isSubmitShowDataSeq.task;
this.isStartSelected = this.isSubmitShowDataSeq.isStartSelected; this.isStartSelected = this.isSubmitShowDataSeq.isStartSelected;
this.isEndSelected = this.isSubmitShowDataSeq.isEndSelected; this.isEndSelected = this.isSubmitShowDataSeq.isEndSelected;
// this.taskStart = this.isSubmitShowDataSeq.taskStart;
// this.taskEnd = this.isSubmitShowDataSeq.taskEnd;
break; break;
case 'Processing time': case 'Processing time':
switch (this.selectedProcessScope) { switch (this.selectedProcessScope) {
@@ -370,15 +372,11 @@ export default {
this.task = this.isSubmitShowDataPtEte.task; this.task = this.isSubmitShowDataPtEte.task;
this.isStartSelected = this.isSubmitShowDataPtEte.isStartSelected; this.isStartSelected = this.isSubmitShowDataPtEte.isStartSelected;
this.isEndSelected = this.isSubmitShowDataPtEte.isEndSelected; this.isEndSelected = this.isSubmitShowDataPtEte.isEndSelected;
// this.taskStart = this.isSubmitShowDataPtEte.taskStart;
// this.taskEnd = this.isSubmitShowDataPtEte.taskEnd;
break; break;
case 'Partial': case 'Partial':
this.task = this.isSubmitShowDataPtP.task; this.task = this.isSubmitShowDataPtP.task;
this.isStartSelected = this.isSubmitShowDataPtP.isStartSelected; this.isStartSelected = this.isSubmitShowDataPtP.isStartSelected;
this.isEndSelected = this.isSubmitShowDataPtP.isEndSelected; this.isEndSelected = this.isSubmitShowDataPtP.isEndSelected;
// this.taskStart = this.isSubmitShowDataPtP.taskStart;
// this.taskEnd = this.isSubmitShowDataPtP.taskEnd;
break; break;
default: default:
break; break;
@@ -390,25 +388,20 @@ export default {
this.task = this.isSubmitShowDataWtEte.task; this.task = this.isSubmitShowDataWtEte.task;
this.isStartSelected = this.isSubmitShowDataWtEte.isStartSelected; this.isStartSelected = this.isSubmitShowDataWtEte.isStartSelected;
this.isEndSelected = this.isSubmitShowDataWtEte.isEndSelected; this.isEndSelected = this.isSubmitShowDataWtEte.isEndSelected;
// this.taskStart = this.isSubmitShowDataWtEte.taskStart;
// this.taskEnd = this.isSubmitShowDataWtEte.taskEnd;
break; break;
case 'Partial': case 'Partial':
this.task = this.isSubmitShowDataWtP.task; this.task = this.isSubmitShowDataWtP.task;
this.isStartSelected = this.isSubmitShowDataWtP.isStartSelected; this.isStartSelected = this.isSubmitShowDataWtP.isStartSelected;
this.isEndSelected = this.isSubmitShowDataWtP.isEndSelected; this.isEndSelected = this.isSubmitShowDataWtP.isEndSelected;
// this.taskStart = this.isSubmitShowDataWtP.taskStart;
// this.taskEnd = this.isSubmitShowDataWtP.taskEnd;
break; break;
default: default:
break; break;
} }
break;
case 'Cycle time': case 'Cycle time':
this.task = this.isSubmitShowDataCt.task; this.task = this.isSubmitShowDataCt.task;
this.isStartSelected = this.isSubmitShowDataCt.isStartSelected; this.isStartSelected = this.isSubmitShowDataCt.isStartSelected;
this.isEndSelected = this.isSubmitShowDataCt.isEndSelected; this.isEndSelected = this.isSubmitShowDataCt.isEndSelected;
// this.taskStart = this.isSubmitShowDataCt.taskStart;
// this.taskEnd = this.isSubmitShowDataCt.taskEnd;
break; break;
default: default:
break; break;

View File

@@ -14,6 +14,7 @@
</p> </p>
<div class="overflow-y-scroll overflow-x-hidden scrollbar mx-[-8px] max-h-[calc(100%_-_96px)]" > <div class="overflow-y-scroll overflow-x-hidden scrollbar mx-[-8px] max-h-[calc(100%_-_96px)]" >
<table class="border-separate border-spacing-x-2 text-sm"> <table class="border-separate border-spacing-x-2 text-sm">
<caption class="hidden">Trace List</caption>
<thead class="sticky top-0 z-10 bg-neutral-100"> <thead class="sticky top-0 z-10 bg-neutral-100">
<tr> <tr>
<th class="h2 px-2 border-b border-neutral-500">Trace</th> <th class="h2 px-2 border-b border-neutral-500">Trace</th>
@@ -285,7 +286,8 @@ export default {
@apply sticky top-0 left-0 z-10 bg-neutral-10 @apply sticky top-0 left-0 z-10 bg-neutral-10
} }
:deep(.p-datatable .p-datatable-thead > tr > th) { :deep(.p-datatable .p-datatable-thead > tr > th) {
@apply !border-y-0 border-neutral-500 bg-neutral-100 after:absolute after:left-0 after:w-full after:h-full after:block after:top-0 after:border-b after:border-t after:border-neutral-500 @apply !border-y-0 border-neutral-500 bg-neutral-100 after:absolute after:left-0 after:w-full after:h-full after:block after:top-0 after:border-b after:border-t after:border-neutral-500;
white-space: nowrap;
} }
:deep(.p-datatable .p-datatable-tbody > tr > td) { :deep(.p-datatable .p-datatable-tbody > tr > td) {
@apply border-neutral-500 !border-t-0 text-center @apply border-neutral-500 !border-t-0 text-center
@@ -294,9 +296,6 @@ export default {
:deep(.p-column-header-content) { :deep(.p-column-header-content) {
@apply justify-center @apply justify-center
} }
:deep(.p-datatable .p-datatable-thead > tr > th) {
white-space: nowrap;
}
:deep(.p-datatable.p-datatable-gridlines .p-datatable-tbody > tr > td) { :deep(.p-datatable.p-datatable-gridlines .p-datatable-tbody > tr > td) {
min-width: 72px; min-width: 72px;
max-width: 184px; max-width: 184px;

View File

@@ -7,6 +7,7 @@
<!-- Table --> <!-- Table -->
<div class="overflow-y-auto overflow-x-auto scrollbar -mx-2 h-[calc(100%_-_64px)]"> <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 text-sm" :class="data.length === 0? 'h-full': null"> <table class="border-separate border-spacing-x-2 table-auto min-w-full text-sm" :class="data.length === 0? 'h-full': null">
<caption class="hidden">Activity List</caption>
<thead class="sticky top-0 left-0 z-10 bg-neutral-10"> <thead class="sticky top-0 left-0 z-10 bg-neutral-10">
<tr> <tr>
<th class="text-start font-semibold leading-10 px-2 border-b border-neutral-500">Activity</th> <th class="text-start font-semibold leading-10 px-2 border-b border-neutral-500">Activity</th>
@@ -85,8 +86,8 @@ export default {
data: function() { data: function() {
// Activity List 要排序 // Activity List 要排序
this.filteredData = this.filteredData.sort((x, y) => { this.filteredData = this.filteredData.sort((x, y) => {
y.occurrences - x.occurrences const diff = y.occurrences - x.occurrences;
if(y.occurrences === x.occurrences) sortNumEngZhtwForFilter(x.label, y.label); return diff !== 0 ? diff : sortNumEngZhtwForFilter(x.label, y.label);
}); });
return this.filteredData; return this.filteredData;
} }
@@ -147,7 +148,9 @@ export default {
// 拖曳結束要顯示箭頭,但最後一個不用 // 拖曳結束要顯示箭頭,但最後一個不用
const lastChild = evt.item.lastChild; const lastChild = evt.item.lastChild;
const listIndex = this.listSequence.length - 1 const listIndex = this.listSequence.length - 1
evt.oldIndex !== listIndex ? lastChild.style.display = '' : null; if (evt.oldIndex !== listIndex) {
lastChild.style.display = '';
}
// reset: 拖曳最後一個元素時,倒數第二的元素的箭頭要隱藏 // reset: 拖曳最後一個元素時,倒數第二的元素的箭頭要隱藏
this.lastItemIndex = null; this.lastItemIndex = null;
}, },

View File

@@ -2,8 +2,6 @@
<div class="bg-neutral-10 border border-neutral-300 rounded-xl px-4 w-full h-full"> <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"> <div class="flex justify-between items-center my-2 flex-wrap">
<p class="h2">{{ tableTitle }}&nbsp({{ tableData.length }})</p> <p class="h2">{{ tableTitle }}&nbsp({{ tableData.length }})</p>
<!-- Search -->
<!-- <Search></Search> -->
</div> </div>
<!-- Table --> <!-- Table -->
<div class="overflow-y-auto overflow-x-auto scrollbar -mx-2 h-[calc(100%_-_64px)]"> <div class="overflow-y-auto overflow-x-auto scrollbar -mx-2 h-[calc(100%_-_64px)]">

View File

@@ -2,8 +2,6 @@
<div class="bg-neutral-10 border border-neutral-300 rounded-xl px-4 h-full"> <div class="bg-neutral-10 border border-neutral-300 rounded-xl px-4 h-full">
<div class="flex justify-between items-center my-2"> <div class="flex justify-between items-center my-2">
<p class="h2">{{ tableTitle }}&nbsp({{ data.length }})</p> <p class="h2">{{ tableTitle }}&nbsp({{ data.length }})</p>
<!-- Search -->
<!-- <Search></Search> -->
</div> </div>
<!-- Table --> <!-- Table -->
<div class="overflow-y-auto overflow-x-auto scrollbar -mx-2 h-[calc(100%_-_64px)]"> <div class="overflow-y-auto overflow-x-auto scrollbar -mx-2 h-[calc(100%_-_64px)]">

View File

@@ -134,6 +134,9 @@ export default {
return { filterAttrs } return { filterAttrs }
}, },
components: {
InputNumber,
},
data() { data() {
return { return {
selectedAttName: {}, selectedAttName: {},
@@ -182,9 +185,12 @@ export default {
attRangeTotal: function() { attRangeTotal: function() {
const type = this.selectedAttName.type; const type = this.selectedAttName.type;
return !this.classTypes.includes(type) ? null let result = null; // Initialize the result variable with null
: this.attRangeData ? `(${this.attRangeData.length})`
: null; if (this.classTypes.includes(type) && this.attRangeData) {
result = `(${this.attRangeData.length})`; // Assign the length of attRangeData if it exists
}
return result;
}, },
attRangeData: function() { attRangeData: function() {
let data = []; let data = [];
@@ -203,7 +209,12 @@ export default {
occ_ratio: this.getPercentLabel(ratio), occ_ratio: this.getPercentLabel(ratio),
freq: item.freq freq: item.freq
}; };
result.label = type !== 'boolean' ? null : item.value ? 'Yes' : 'No'; result.label = null;
if (type === 'boolean') {
result.label = item.value ? 'Yes' : 'No';
} else {
result.label = null;
}
return result; return result;
}) })
return data.sort((x, y) => y.freq - x.freq); return data.sort((x, y) => y.freq - x.freq);
@@ -224,6 +235,7 @@ export default {
let max = this.valueData.max; let max = this.valueData.max;
const type = this.valueData.type; const type = this.valueData.type;
switch (type) { switch (type) {
case 'dummy':
case 'date': case 'date':
xAxisMin = new Date(min).getTime(); xAxisMin = new Date(min).getTime();
xAxisMax = new Date(max).getTime(); xAxisMax = new Date(max).getTime();
@@ -268,6 +280,7 @@ export default {
const type = this.selectedAttName.type; const type = this.selectedAttName.type;
switch (type) { switch (type) {
case 'dummy': //sonar-qube
case 'date': case 'date':
start = getMoment(this.startTime).format('YYYY-MM-DDTHH:mm:00'); start = getMoment(this.startTime).format('YYYY-MM-DDTHH:mm:00');
end = getMoment(this.endTime).format('YYYY-MM-DDTHH:mm:00'); end = getMoment(this.endTime).format('YYYY-MM-DDTHH:mm:00');
@@ -366,6 +379,7 @@ export default {
const min = this.valueData.min; const min = this.valueData.min;
const max = this.valueData.max; const max = this.valueData.max;
switch (this.selectedAttName.type) { switch (this.selectedAttName.type) {
case 'dummy': //sonar-qube
case 'date': case 'date':
// 除了 date 外雙向綁定為空 // 除了 date 外雙向綁定為空
this.valueStart = null; this.valueStart = null;
@@ -394,7 +408,7 @@ export default {
break; break;
} }
// 傳給後端 // 傳給後端
this.attValueTypeStartEnd; // this.attValueTypeStartEnd; 是否有要呼叫函數? sonar-qube
// 建立圖表 // 建立圖表
this.createChart(); this.createChart();
} }
@@ -470,9 +484,15 @@ export default {
break; break;
case 'float': case 'float':
setLabels = data.map((item, index) => { setLabels = data.map((item, index) => {
let x = index === 0 ? Math.floor(item.x * 100) / 100 : let x;
index === data.length - 1 ? item.x = Math.ceil(item.x * 100) / 100 : if (index === 0) {
Math.round(item.x * 100) / 100; x = Math.floor(item.x * 100) / 100;
} else if (index === data.length - 1) {
item.x = Math.ceil(item.x * 100) / 100;
x = item.x;
} else {
x = Math.round(item.x * 100) / 100;
}
return x return x
}); });
break; break;
@@ -574,13 +594,21 @@ export default {
maxRotation: 0, // 不旋轉 lable 0~50 maxRotation: 0, // 不旋轉 lable 0~50
color: '#334155', color: '#334155',
callback: ((value, index, values) => { callback: ((value, index, values) => {
let x;
switch (valueData.type) { switch (valueData.type) {
case 'int': case 'int':
return Math.round(value); return Math.round(value);
case 'float': case 'float':
let x = index === 0 ? Math.floor(value * 100) / 100 : switch (index) {
index === values.length - 1 ? value = Math.ceil(value * 100) / 100 : case 0:
Math.round(value * 100) / 100; x = Math.floor(value * 100) / 100;
break;
case values.length - 1:
x = Math.ceil(value * 100) / 100;
break;
default:
x = Math.round(value * 100) / 100;
}
// 處理科學記號等格式轉換 // 處理科學記號等格式轉換
// Decimal 無法處理超過 16 位數 // Decimal 無法處理超過 16 位數
x = new Intl.NumberFormat(undefined, {useGrouping: false}).format(x); x = new Intl.NumberFormat(undefined, {useGrouping: false}).format(x);
@@ -607,6 +635,7 @@ export default {
let end = sliderData[e[1].toFixed()]; // 取得 index須為整數。 let end = sliderData[e[1].toFixed()]; // 取得 index須為整數。
switch (this.selectedAttName.type) { switch (this.selectedAttName.type) {
case 'dummy':
case 'date': case 'date':
this.startTime = new Date(start); this.startTime = new Date(start);
this.endTime = new Date(end); this.endTime = new Date(end);
@@ -625,7 +654,7 @@ export default {
// 重新算圖 // 重新算圖
this.resizeMask(this.chartComplete); this.resizeMask(this.chartComplete);
// 執行 timeFrameStartEnd 才會改變數據 // 執行 timeFrameStartEnd 才會改變數據
this.attValueTypeStartEnd; // this.attValueTypeStartEnd; 是否有要呼叫函數? sonar-qube
}, },
/** /**
* 選取開始或結束時間時,要改變滑塊跟圖表 * 選取開始或結束時間時,要改變滑塊跟圖表
@@ -653,8 +682,20 @@ export default {
this.selectArea = closestIndexes; this.selectArea = closestIndexes;
// 重新設定 start end 日曆選取範圍 // 重新設定 start end 日曆選取範圍
if(!isDateType) inputValue = Number(e.value.replace(/,/g, '')) ; if(!isDateType) inputValue = Number(e.value.replace(/,/g, '')) ;
if(direction === 'start') isDateType ? this.endMinDate = e : this.valueEndMin = inputValue; if(direction === 'start') {
else if(direction === 'end') isDateType ? this.startMaxDate = e : this.valueStartMax = inputValue; if(isDateType){
this.endMinDate = e;
} else {
this.valueEndMin = inputValue;
}
}
else if(direction === 'end') {
if(isDateType) {
this.startMaxDate = e;
} else {
this.valueStartMax = inputValue;
};
}
// 重新算圖 // 重新算圖
if(!isNaN(closestIndexes[0]) && !isNaN(closestIndexes[1])) this.resizeMask(this.chartComplete); if(!isNaN(closestIndexes[0]) && !isNaN(closestIndexes[1])) this.resizeMask(this.chartComplete);
else return; else return;

View File

@@ -50,7 +50,7 @@ export default {
const { isLoading } = storeToRefs(loadingStore); const { isLoading } = storeToRefs(loadingStore);
const { hasResultRule, temporaryData, postRuleData, ruleData, isRuleData, tempFilterId } = storeToRefs(allMapDataStore); const { hasResultRule, temporaryData, postRuleData, ruleData, isRuleData, tempFilterId } = storeToRefs(allMapDataStore);
return { isLoading, hasResultRule, temporaryData, postRuleData, ruleData, isRuleData, allMapDataStore, tempFilterId, allMapDataStore } return { isLoading, hasResultRule, temporaryData, postRuleData, ruleData, isRuleData, allMapDataStore, tempFilterId }
}, },
methods: { methods: {
/** /**

View File

@@ -103,17 +103,20 @@ export default{
let d = this.filterTimeframe.data[0].y; let d = this.filterTimeframe.data[0].y;
let e = 2; let e = 2;
let f = this.filterTimeframe.data[1].y; let f = this.filterTimeframe.data[1].y;
b = (e*d - a*d - f*a - f*c) / (e - c - a) b = (e*d - a*d - f*a - f*c) / (e - c - a);
b < 0 ? b = 0 : b; if(b < 0) {
b = 0;
}
// y 軸最大值 // y 軸最大值
let ma = 9; let ma = 9;
let mb = this.filterTimeframe.data[8].y; let mb = this.filterTimeframe.data[8].y;
let mc = 10; let mc = 10;
let md = this.filterTimeframe.data[9].y; let md = this.filterTimeframe.data[9].y;
let me = 11; let me = 11;
let mf; let mf = (mb*me - mb*mc -md*me + md*ma) / (ma - mc);
mf = (mb*me - mb*mc -md*me + md*ma) / (ma - mc); if(mf < 0) {
mf < 0 ? mf = 0 : mf; mf = 0;
}
// 添加最小值 // 添加最小值
data.unshift({ data.unshift({
@@ -304,7 +307,7 @@ export default{
// 重新算圖 // 重新算圖
this.resizeMask(this.chart); this.resizeMask(this.chart);
// 執行 timeFrameStartEnd 才會改變數據 // 執行 timeFrameStartEnd 才會改變數據
this.timeFrameStartEnd; this.timeFrameStartEnd();
}, },
/** /**
* 選取開始或結束時間時,要改變滑塊跟圖表 * 選取開始或結束時間時,要改變滑塊跟圖表
@@ -347,7 +350,7 @@ export default{
// 讓日曆的範圍等於時間軸的範圍 // 讓日曆的範圍等於時間軸的範圍
this.startTime = this.startMinDate; this.startTime = this.startMinDate;
this.endTime = this.startMaxDate; this.endTime = this.startMaxDate;
this.timeFrameStartEnd; this.timeFrameStartEnd();
}, },
} }
</script> </script>

View File

@@ -21,6 +21,7 @@
</p> </p>
<div class="overflow-y-scroll overflow-x-hidden scrollbar mx-[-8px] max-h-[calc(100%_-_96px)]" > <div class="overflow-y-scroll overflow-x-hidden scrollbar mx-[-8px] max-h-[calc(100%_-_96px)]" >
<table class="border-separate border-spacing-x-2 text-sm w-full"> <table class="border-separate border-spacing-x-2 text-sm w-full">
<caption class="hidden">Trace list</caption>
<thead class="sticky top-0 z-10 bg-neutral-10"> <thead class="sticky top-0 z-10 bg-neutral-10">
<tr> <tr>
<th class="h2 px-2 border-b border-neutral-500">Trace</th> <th class="h2 px-2 border-b border-neutral-500">Trace</th>
@@ -161,7 +162,7 @@ export default {
{ field: 'id', header: 'Case Id' }, { field: 'id', header: 'Case Id' },
{ field: 'started_at', header: 'Start time' }, { field: 'started_at', header: 'Start time' },
{ field: 'completed_at', header: 'End time' }, { field: 'completed_at', header: 'End time' },
...data[0]?.attributes.map((att, index) => ({ field: `att_${index}`, header: att.key })), ...(data[0]?.attributes ?? []).map((att, index) => ({ field: `att_${index}`, header: att.key })),
]; ];
} }
return result return result
@@ -256,7 +257,6 @@ export default {
async switchCaseData(id, count) { async switchCaseData(id, count) {
// 點同一筆 id 不要有動作 // 點同一筆 id 不要有動作
if(id == this.showTraceId) return; if(id == this.showTraceId) return;
// if(count >= 1000) this.isLoading = true; // 超過 1000 筆要 loading 畫面
this.isLoading = true; // 都要 loading 畫面 this.isLoading = true; // 都要 loading 畫面
this.infinit404 = null; this.infinit404 = null;
this.infinitMaxItems = false; this.infinitMaxItems = false;
@@ -363,14 +363,12 @@ export default {
@apply sticky top-0 left-0 z-10 bg-neutral-10 @apply sticky top-0 left-0 z-10 bg-neutral-10
} }
:deep(.p-datatable .p-datatable-thead > tr > th) { :deep(.p-datatable .p-datatable-thead > tr > th) {
@apply !border-y-0 border-neutral-500 bg-neutral-100 after:absolute after:left-0 after:w-full after:h-full after:block after:top-0 after:border-b after:border-t after:border-neutral-500 @apply !border-y-0 border-neutral-500 bg-neutral-100 after:absolute after:left-0 after:w-full after:h-full after:block after:top-0 after:border-b after:border-t after:border-neutral-500;
white-space: nowrap;
} }
:deep(.p-datatable .p-datatable-tbody > tr > td) { :deep(.p-datatable .p-datatable-tbody > tr > td) {
@apply border-neutral-500 !border-t-0 text-center @apply border-neutral-500 !border-t-0 text-center
} }
:deep(.p-datatable .p-datatable-thead > tr > th) {
white-space: nowrap;
}
:deep(.p-datatable.p-datatable-gridlines .p-datatable-tbody > tr > td) { :deep(.p-datatable.p-datatable-gridlines .p-datatable-tbody > tr > td) {
min-width: 72px; min-width: 72px;
max-width: 184px; max-width: 184px;

View File

@@ -38,14 +38,6 @@
<label :for="item + index">{{ item }}</label> <label :for="item + index">{{ item }}</label>
</div> </div>
</div> </div>
<!-- 目前只有 case -->
<!-- <div v-show="selectValue[0] === 'Attributes'">
<p class="h2">Mode</p>
<div v-for="(item, index) in selectFilter['ModeAtt']" :key="index">
<RadioButton v-model="selectValue[4]" :inputId="item + index" name="ModeAtt" :value="item" class="mb-1 mr-2"/>
<label :for="item + index">{{ item }}</label>
</div>
</div> -->
<div> <div>
<p class="h2">Refine</p> <p class="h2">Refine</p>
<div v-for="(item, index) in selectFilter['Refine']" :key="index"> <div v-for="(item, index) in selectFilter['Refine']" :key="index">
@@ -218,60 +210,22 @@ export default {
// Apply Button disabled setting // Apply Button disabled setting
isDisabledButton: function() { isDisabledButton: function() {
let disabled = true; let disabled = true;
let sele = this.selectValue; const { selectValue: sele, selectAttType: type } = this;
const type = this.selectAttType; const firstSelection = sele[0];
switch(sele[0]) { if (firstSelection === 'Sequence') {
case 'Sequence': // Filter Type 選 Sequence 的行為 disabled = this.handleSequenceSelection(sele);
switch(sele[1]) { } else if (firstSelection === 'Attributes') {
case 'Have activity(s)': // Activity Sequence 選 Have activity(s) 的行為 disabled = this.handleAttributesSelection(type);
if(this.selectFilterTask && this.selectFilterTask?.length !== 0) disabled = false; } else if (firstSelection === 'Trace') {
break; disabled = this.handleTraceSelection();
case 'Start & End': // Activity Sequence 選 Start & End 的行為 } else if (firstSelection === 'Timeframes') {
switch(sele[2]) { disabled = this.handleTimeframesSelection();
case 'Start':
if(this.selectFilterStart) disabled = false;
break;
case 'End':
if(this.selectFilterEnd) disabled = false;
break;
case 'Start & End':
if(this.selectFilterStartToEnd && this.selectFilterEndToStart) disabled = false;
break;
}
break;
case 'Sequence': // Activity Sequence 選 Sequence 的行為
if(this.listSeq.length >= 2) disabled = false;
break;
}
break;
case 'Attributes': // Activity Sequence 選 Attributes 的行為
switch (type) {
case 'string':
if(this.selectAttribute && this.selectAttribute.length > 0) disabled = false;
break;
case 'boolean':
if(this.selectAttribute?.key && this.selectAttribute?.label) disabled = false;
break;
case 'int':
case 'float':
case 'date':
if(this.selectAttribute?.key && this.selectAttribute?.min != null && this.selectAttribute?.max != null) disabled = false;
break;
default:
break;
}
break;
case 'Trace': // Filter Type 選 Trace 的行為
if(this.selectTraceArea[0] !== this.selectTraceArea[1]) disabled = false;
break;
case 'Timeframes': // Filter Type 選 Timeframes 的行為
if(this.selectTimeFrame.length > 0) disabled = false;
break;
} }
this.isDisabled = disabled; this.isDisabled = disabled;
return disabled; return disabled;
} },
}, },
methods: { methods: {
/** /**
@@ -442,57 +396,36 @@ export default {
'occurred-around' : 'active in' 'occurred-around' : 'active in'
}; };
switch(e.type){ switch(e.type) { //sonar-qube
case "contains-task": case "contains-task":
label = `${includeStr}, ${e.task}`;
type = "Sequence";
break;
case "starts-with": case "starts-with":
label = `${includeStr}, start with ${e.task}`;
type = "Sequence";
break;
case "ends-with": case "ends-with":
label = `${includeStr}, end with ${e.task}`; case "directly-follows":
case "eventually-follows":
label = `${includeStr}, ${getTaskLabel(e)}`;
type = "Sequence"; type = "Sequence";
break; break;
case "start-end": case "start-end":
label = `${includeStr}, start with ${e.starts_with}, end with ${e.ends_with}`; label = `${includeStr}, start with ${e.starts_with}, end with ${e.ends_with}`;
type = "Sequence"; type = "Sequence";
break; break;
case "directly-follows":
label = `${includeStr}, directly follows, ${e.task_seq.join(' -> ')}`;
type = "Sequence";
break;
case "eventually-follows":
label = `${includeStr}, eventually follows, ${e.task_seq.join(' -> ')}`;
type = "Sequence";
break;
case "trace-freq": case "trace-freq":
label = `${includeStr}, from #${e.lower} to #${e.upper}`; label = `${includeStr}, from #${e.lower} to #${e.upper}`;
type = "Trace"; type = "Trace";
break; break;
case 'string-attr': case 'string-attr':
label = `${includeStr}, ${e.key}, ${e.value}`;
type = "Attributes";
break;
case 'boolean-attr': case 'boolean-attr':
label = `${includeStr}, ${e.key}, ${this.selectAttribute?.label}`;
type = "Attributes";
break;
case 'int-attr': case 'int-attr':
case 'float-attr': case 'float-attr':
label = `${includeStr}, ${e.key}, from ${e.min} to ${e.max}`;
type = "Attributes";
break;
case 'date-attr': case 'date-attr':
label = `${includeStr}, ${e.key}, from ${getMoment(e.min).format('YYYY-MM-DD HH:mm')} to ${getMoment(e.max).format('YYYY-MM-DD HH:mm')}`; label = `${includeStr}, ${getAttributeLabel(e)}`;
type = "Attributes"; type = "Attributes";
break; break;
case "occurred-in": case "occurred-in":
case "started-in": case "started-in":
case "completed-in": case "completed-in":
case "occurred-around": case "occurred-around":
label = `${containmentMap[e.type]}, ${includeStr}, from ${getMoment(e.start).format("YYYY-MM-DD HH:mm")} to ${getMoment(e.end).format("YYYY-MM-DD HH:mm")} ` label = `${containmentMap[e.type]}, ${includeStr}, from ${getMoment(e.start).format("YYYY-MM-DD HH:mm")} to ${getMoment(e.end).format("YYYY-MM-DD HH:mm")} `;
type = "Timeframe" type = "Timeframe"
break; break;
}; };
@@ -528,7 +461,9 @@ export default {
this.$refs.filterTraceView.selectArea = [0, this.$refs.filterTraceView.traceTotal]; this.$refs.filterTraceView.selectArea = [0, this.$refs.filterTraceView.traceTotal];
}; };
// 成功訊息 // 成功訊息
massage ? this.$toast.success('Filter cleared.') : null; if(message) {
this.$toast.success('Filter cleared.')
}
}, },
/** /**
* header:Filter 發送選取的資料 * header:Filter 發送選取的資料
@@ -537,7 +472,7 @@ export default {
this.isLoading = true; this.isLoading = true;
let data; let data;
let sele = this.selectValue; let sele = this.selectValue;
let isExclude = sele[5] === 'Exclude' ? true : false; let isExclude = sele[5] === 'Exclude';
let containmentMap = { let containmentMap = {
'Contained in': 'occurred-in', 'Contained in': 'occurred-in',
'Started in': 'started-in', 'Started in': 'started-in',
@@ -641,9 +576,9 @@ export default {
break break
} }
break; break;
case 'Trace': // Filter Type 選 Trace 的行為 case 'Trace': { // Filter Type 選 Trace 的行為
let lowerIndex = this.$refs.filterTraceView.selectArea[0]; const lowerIndex = this.$refs.filterTraceView.selectArea[0];
let upperIndex = this.$refs.filterTraceView.selectArea[1]-1; const upperIndex = this.$refs.filterTraceView.selectArea[1]-1;
data = { data = {
type: 'trace-freq', type: 'trace-freq',
lower: this.allMapDataStore.traces[lowerIndex].id, lower: this.allMapDataStore.traces[lowerIndex].id,
@@ -651,6 +586,7 @@ export default {
is_exclude: isExclude, is_exclude: isExclude,
}; };
break; break;
}
case 'Timeframes': // Filter Type 選 Timeframes 的行為 case 'Timeframes': // Filter Type 選 Timeframes 的行為
data = { data = {
type: containmentMap[sele[6]], type: containmentMap[sele[6]],
@@ -668,7 +604,10 @@ export default {
await this.allMapDataStore.checkHasResult(); await this.allMapDataStore.checkHasResult();
// 有 Data 就加進 Funnel沒有 Data 不加進 Funnel 和跳錯誤訊息 // 有 Data 就加進 Funnel沒有 Data 不加進 Funnel 和跳錯誤訊息
if(this.hasResultRule === null) return this.isLoading = false; if(this.hasResultRule === null) {
this.isLoading = false;
return;
}
else if(this.hasResultRule) { else if(this.hasResultRule) {
if(!this.temporaryData?.length){ if(!this.temporaryData?.length){
this.temporaryData.push(...postData); this.temporaryData.push(...postData);
@@ -680,13 +619,11 @@ export default {
this.ruleData.push(...postData.map(e => this.setRule(e))) this.ruleData.push(...postData.map(e => this.setRule(e)))
} }
this.reset(false); this.reset(false);
// this.isLoading = true;
await new Promise(resolve => setTimeout(resolve, 1000)); await new Promise(resolve => setTimeout(resolve, 1000));
this.isLoading = false; this.isLoading = false;
this.$toast.success('Filter applied. Go to Funnel to verify.'); this.$toast.success('Filter applied. Go to Funnel to verify.');
}else { }else {
this.reset(false); this.reset(false);
// this.isLoading = true;
await new Promise(resolve => setTimeout(resolve, 1000)); await new Promise(resolve => setTimeout(resolve, 1000));
this.isLoading = false; this.isLoading = false;
this.$toast.warning('No result.'); this.$toast.warning('No result.');
@@ -698,6 +635,78 @@ export default {
sumbitAll() { sumbitAll() {
this.$emit('submit-all'); this.$emit('submit-all');
}, },
handleSequenceSelection(sele) {
const secondSelection = sele[1];
switch (secondSelection) {
case 'Have activity(s)':
return !(this.selectFilterTask && this.selectFilterTask.length !== 0);
case 'Start & End':
return this.handleStartEndSelection(sele[2]);
case 'Sequence':
return this.listSeq.length < 2;
default:
return true;
}
},
handleStartEndSelection(option) {
switch (option) {
case 'Start':
return !this.selectFilterStart;
case 'End':
return !this.selectFilterEnd;
case 'Start & End':
return !(this.selectFilterStartToEnd && this.selectFilterEndToStart);
default:
return true;
}
},
handleAttributesSelection(type) {
switch (type) {
case 'string':
return !(this.selectAttribute && this.selectAttribute.length > 0);
case 'boolean':
return !(this.selectAttribute?.key && this.selectAttribute?.label);
case 'int':
case 'float':
case 'date':
return !(this.selectAttribute?.key && this.selectAttribute?.min != null && this.selectAttribute?.max != null);
default:
return true;
}
},
handleTraceSelection() {
return this.selectTraceArea[0] === this.selectTraceArea[1];
},
handleTimeframesSelection() {
return this.selectTimeFrame.length === 0;
},
getTaskLabel(e) {
switch (e.type) {
case "contains-task":
return `${e.task}`;
case "starts-with":
return `start with ${e.task}`;
case "ends-with":
return `end with ${e.task}`;
case "directly-follows":
case "eventually-follows":
return `${e.type.replace('-', ' ')}, ${e.task_seq.join(' -> ')}`;
}
},
getAttributeLabel(e) {
switch (e.type) {
case 'string-attr':
return `${e.key}, ${e.value}`;
case 'boolean-attr':
return `${e.key}, ${this.selectAttribute?.label}`;
case 'int-attr':
case 'float-attr':
return `${e.key}, from ${e.min} to ${e.max}`;
case 'date-attr':
return `${e.key}, from ${getMoment(e.min).format('YYYY-MM-DD HH:mm')} to ${getMoment(e.max).format('YYYY-MM-DD HH:mm')}`;
}
},
}, },
} }
</script> </script>

View File

@@ -62,6 +62,8 @@
<div class="pt-1 pb-4"> <div class="pt-1 pb-4">
<p class="h2">Case Duration</p> <p class="h2">Case Duration</p>
<table class="text-sm caseDurationTable"> <table class="text-sm caseDurationTable">
<caption class="hidden">Case Duration</caption>
<th class="hidden"></th>
<tbody> <tbody>
<tr> <tr>
<td> <td>
@@ -189,7 +191,9 @@
</ul> </ul>
</TabPanel> </TabPanel>
<TabPanel header="Most Frequent Trace" contentClass="text-sm"> <TabPanel header="Most Frequent Trace" contentClass="text-sm">
<li v-if="insights.most_freq_traces.length === 0">No data</li> <ul v-if="insights.most_freq_traces.length === 0">
<li >No data</li>
</ul>
<ul v-else class="list-disc ml-6 space-y-1"> <ul v-else class="list-disc ml-6 space-y-1">
<li class="break-words" v-for="(item, key) in insights.most_freq_traces" :key="key"> <li class="break-words" v-for="(item, key) in insights.most_freq_traces" :key="key">
<span v-for="(value, index) in item" :key="index">{{ value }}<span v-if="index !== item.length - 1">&nbsp;<span class="material-symbols-outlined !text-lg align-sub">arrow_forward</span>&nbsp;</span> <span v-for="(value, index) in item" :key="index">{{ value }}<span v-if="index !== item.length - 1">&nbsp;<span class="material-symbols-outlined !text-lg align-sub">arrow_forward</span>&nbsp;</span>
@@ -297,9 +301,6 @@ export default {
:deep(.p-tabview-panel) { :deep(.p-tabview-panel) {
@apply animate-fadein @apply animate-fadein
} }
/* .caseDurationTable td:first-child {
@apply pr-1
} */
.caseDurationTable td { .caseDurationTable td {
@apply scroll-pb-12 @apply scroll-pb-12
} }

View File

@@ -12,6 +12,7 @@
</p> </p>
<div class="overflow-y-scroll overflow-x-hidden scrollbar mx-[-8px] max-h-[calc(100%_-_96px)]" > <div class="overflow-y-scroll overflow-x-hidden scrollbar mx-[-8px] max-h-[calc(100%_-_96px)]" >
<table class="border-separate border-spacing-x-2 text-sm"> <table class="border-separate border-spacing-x-2 text-sm">
<caption class="hidden">Trace List</caption>
<thead class="sticky top-0 z-10 bg-neutral-10"> <thead class="sticky top-0 z-10 bg-neutral-10">
<tr> <tr>
<th class="h2 px-2 border-b border-neutral-500">Trace</th> <th class="h2 px-2 border-b border-neutral-500">Trace</th>
@@ -125,7 +126,7 @@ export default {
{ field: 'id', header: 'Case Id' }, { field: 'id', header: 'Case Id' },
{ field: 'started_at', header: 'Start time' }, { field: 'started_at', header: 'Start time' },
{ field: 'completed_at', header: 'End time' }, { field: 'completed_at', header: 'End time' },
...data[0]?.attributes.map((att, index) => ({ field: `att_${index}`, header: att.key })), ...(data[0]?.attributes ?? []).map((att, index) => ({ field: `att_${index}`, header: att.key })),
]; ];
} }
return result return result
@@ -175,7 +176,6 @@ export default {
async switchCaseData(id, count) { async switchCaseData(id, count) {
// 點同一筆 id 不要有動作 // 點同一筆 id 不要有動作
if(id == this.showTraceId) return; if(id == this.showTraceId) return;
// if(count >= 1000) this.isLoading = true; // 超過 1000 筆要 loading 畫面
this.isLoading = true; // 都要 loading 畫面 this.isLoading = true; // 都要 loading 畫面
this.infinit404 = null; this.infinit404 = null;
this.infinitMaxItems = false; this.infinitMaxItems = false;
@@ -287,14 +287,12 @@ export default {
@apply sticky top-0 left-0 z-10 bg-neutral-10 @apply sticky top-0 left-0 z-10 bg-neutral-10
} }
:deep(.p-datatable .p-datatable-thead > tr > th) { :deep(.p-datatable .p-datatable-thead > tr > th) {
@apply !border-y-0 border-neutral-500 bg-neutral-100 after:absolute after:left-0 after:w-full after:h-full after:block after:top-0 after:border-b after:border-t after:border-neutral-500 @apply !border-y-0 border-neutral-500 bg-neutral-100 after:absolute after:left-0 after:w-full after:h-full after:block after:top-0 after:border-b after:border-t after:border-neutral-500;
white-space: nowrap;
} }
:deep(.p-datatable .p-datatable-tbody > tr > td) { :deep(.p-datatable .p-datatable-tbody > tr > td) {
@apply border-neutral-500 !border-t-0 text-center @apply border-neutral-500 !border-t-0 text-center
} }
:deep(.p-datatable .p-datatable-thead > tr > th) {
white-space: nowrap;
}
:deep(.p-datatable.p-datatable-gridlines .p-datatable-tbody > tr > td) { :deep(.p-datatable.p-datatable-gridlines .p-datatable-tbody > tr > td) {
min-width: 72px; min-width: 72px;
max-width: 184px; max-width: 184px;

View File

@@ -141,13 +141,13 @@ export default {
if(e.target.value !== 'freq' && e.target.value !== 'duration') value = e.target.value; if(e.target.value !== 'freq' && e.target.value !== 'duration') value = e.target.value;
switch (type) { switch (type) {
case 'freq': case 'freq':
value = value ? value : this.selectedFreq ? this.selectedFreq : 'total'; value = value || this.selectedFreq || 'total';
this.dataLayerType = type; this.dataLayerType = type;
this.dataLayerOption = value; this.dataLayerOption = value;
this.selectedFreq = value; this.selectedFreq = value;
break; break;
case 'duration': case 'duration':
value = value ? value : this.selectedDuration ? this.selectedDuration : 'total'; value = value || this.selectedDuration || 'total';
this.dataLayerType = type; this.dataLayerType = type;
this.dataLayerOption = value; this.dataLayerOption = value;
this.selectedDuration = value; this.selectedDuration = value;

View File

@@ -5,7 +5,6 @@
<li class="bg-neutral-10 rounded p-3 w-full"> <li class="bg-neutral-10 rounded p-3 w-full">
<div class="flex justify-between items-center mb-5"> <div class="flex justify-between items-center mb-5">
<p class="font-bold text-sm leading-8">Cases</p> <p class="font-bold text-sm leading-8">Cases</p>
<!-- <span class="material-symbols-outlined text-base">info</span> -->
</div> </div>
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
<div class="mr-2 w-full"> <div class="mr-2 w-full">
@@ -18,7 +17,6 @@
<li class="bg-neutral-10 rounded p-3 w-full"> <li class="bg-neutral-10 rounded p-3 w-full">
<div class="flex justify-between items-center mb-5"> <div class="flex justify-between items-center mb-5">
<p class="font-bold text-sm leading-8">Traces</p> <p class="font-bold text-sm leading-8">Traces</p>
<!-- <span class="material-symbols-outlined text-base">info</span> -->
</div> </div>
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
<div class="mr-2 w-full"> <div class="mr-2 w-full">
@@ -31,7 +29,6 @@
<li class="bg-neutral-10 rounded p-3 w-full"> <li class="bg-neutral-10 rounded p-3 w-full">
<div class="flex justify-between items-center mb-5"> <div class="flex justify-between items-center mb-5">
<p class="font-bold text-sm leading-8">Activity Instances</p> <p class="font-bold text-sm leading-8">Activity Instances</p>
<!-- <span class="material-symbols-outlined text-base">info</span> -->
</div> </div>
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
<div class="mr-2 w-full"> <div class="mr-2 w-full">
@@ -44,7 +41,6 @@
<li class="bg-neutral-10 rounded p-3 w-full"> <li class="bg-neutral-10 rounded p-3 w-full">
<div class="flex justify-between items-center mb-5"> <div class="flex justify-between items-center mb-5">
<p class="font-bold text-sm leading-8">Activities</p> <p class="font-bold text-sm leading-8">Activities</p>
<!-- <span class="material-symbols-outlined text-base">info</span> -->
</div> </div>
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
<div class="mr-2 w-full"> <div class="mr-2 w-full">

View File

@@ -75,7 +75,11 @@ export default {
}, },
}, },
mounted() { mounted() {
this.$route.name === 'Login' || this.$route.name === 'NotFound404' ? this.showMember = false : this.showMember = true; if (this.$route.name === 'Login' || this.$route.name === 'NotFound404') {
this.showMember = false
} else {
this.showMember = true;
}
} }
} }

View File

@@ -24,22 +24,6 @@
</div> </div>
<!-- Files Page: Search and Upload --> <!-- Files Page: Search and Upload -->
<div class="flex justify-end items-center" v-if="navViewName === 'FILES'"> <div class="flex justify-end items-center" v-if="navViewName === 'FILES'">
<!-- <form role="search">
<label for="searchFiles" class="mr-4 relative">
<input type="search" id="searchFiles" placeholder="Search" class="px-5 py-2 w-72 rounded-full text-sm align-middle duration-300 border border-neutral-500 hover:border-neutral-300 focus:outline-none focus:ring focus:border-neutral-300">
<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">
<IconSearch class="w-6 h-6"></IconSearch>
</button>
</span>
</label>
</form> -->
<!-- <label class="btn btn-sm btn-neutral cursor-pointer">
<input id="uploadFiles" class="hidden" type="file">
Upload
</label> -->
<div id="import_btn" class="btn btn-sm btn-neutral cursor-pointer" @click="uploadModal = true"> <div id="import_btn" class="btn btn-sm btn-neutral cursor-pointer" @click="uploadModal = true">
Import Import
<UploadModal :visible="uploadModal" @closeModal="uploadModal = $event"></UploadModal> <UploadModal :visible="uploadModal" @closeModal="uploadModal = $event"></UploadModal>
@@ -54,7 +38,6 @@
:disabled="disabledSave" @click="saveModal"> :disabled="disabledSave" @click="saveModal">
Save Save
</button> </button>
<!-- <button class="btn btn-sm btn-neutral">Download</button> -->
</div> </div>
<AcctMenu/> <AcctMenu/>
</div> </div>
@@ -122,16 +105,16 @@ export default {
case 'Map': case 'Map':
case 'CheckMap': case 'CheckMap':
// 沒有 filter Id, 沒有暫存 tempFilterId Id 就不能存檔 // 沒有 filter Id, 沒有暫存 tempFilterId Id 就不能存檔
return this.tempFilterId ? false : true; return !this.tempFilterId;
case 'Conformance': case 'Conformance':
case 'CheckConformance': case 'CheckConformance':
return this.conformanceFilterTempCheckId || this.conformanceLogTempCheckId ? false : true; return !(this.conformanceFilterTempCheckId || this.conformanceLogTempCheckId);
} }
}, },
showIcon: function() { showIcon: function() {
let result = true; let result = true;
result = ['FILES', 'UPLOAD'].includes(this.navViewName) ? false : true; result = !['FILES', 'UPLOAD'].includes(this.navViewName);
return result; return result;
}, },
noShowSaveButton: function() { noShowSaveButton: function() {
@@ -157,7 +140,7 @@ export default {
if(this.$route.params.type === 'filter') { if(this.$route.params.type === 'filter') {
this.createFilterId= this.$route.params.fileId; this.createFilterId= this.$route.params.fileId;
} }
this.showNavbarBreadcrumb = this.$route.matched[0].name !== ('AuthContainer') ? true : false; this.showNavbarBreadcrumb = this.$route.matched[0].name !== ('AuthContainer');
this.getNavViewName(); this.getNavViewName();
}, },
methods: { methods: {
@@ -251,9 +234,12 @@ export default {
break; break;
case 'COMPARE': case 'COMPARE':
switch(name) { switch(name) {
case 'dummy':
case 'CompareDashboard': case 'CompareDashboard':
valueToSet = 'DASHBOARD'; valueToSet = 'DASHBOARD';
break; break;
default:
break;
} }
break; break;
} }
@@ -274,7 +260,6 @@ export default {
*/ */
async saveModal() { async saveModal() {
// 協助判斷 MAP, CONFORMANCE 儲存有「送出」或「取消」。 // 協助判斷 MAP, CONFORMANCE 儲存有「送出」或「取消」。
let isSaved;
// 傳給 Map通知 Sidebar 要關閉。 // 傳給 Map通知 Sidebar 要關閉。
this.$emitter.emit('saveModal', false); this.$emitter.emit('saveModal', false);

View File

@@ -1,7 +1,9 @@
<template> <template>
<form role="search"> <form role="search">
<label for="searchFiles" class="mr-4 relative"> <label for="searchFiles" class="mr-4 relative" htmlFor="searchFiles">
<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"> <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"> <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> <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> <span class="w-px h-6 block after:border after:border-neutral-300 after:content-['']"></span>

View File

@@ -19,6 +19,7 @@
v-closable="{id: size, handler: onClose}"> v-closable="{id: size, handler: onClose}">
<div class="duration-box" v-for="(unit, index) in inputTypes" :key="unit"> <div class="duration-box" v-for="(unit, index) in inputTypes" :key="unit">
<input <input
id="input_duration_dhms"
type="text" type="text"
class="duration duration-val input-dhms-field" class="duration duration-val input-dhms-field"
:data-index="index" :data-index="index"
@@ -32,7 +33,7 @@
@keyup="onKeyUp" @keyup="onKeyUp"
v-model="inputTimeFields[index]" v-model="inputTimeFields[index]"
/> />
<label class="duration">{{ tUnits[unit].dsp }}</label> <label class="duration" for="input_duration_dhms">{{ tUnits[unit].dsp }}</label>
</div> </div>
</div> </div>
</div> </div>
@@ -123,7 +124,6 @@ export default {
this[unit] = newValues[unit].val; this[unit] = newValues[unit].val;
const input = document.querySelector(`[data-tunit="${unit}"]`); const input = document.querySelector(`[data-tunit="${unit}"]`);
if (input) { if (input) {
// input.value = newValues[unit].val.toString().padStart(2, '0'); // 前綴要補 0
input.value = newValues[unit].val.toString(); input.value = newValues[unit].val.toString();
} }
} }
@@ -144,14 +144,18 @@ export default {
max: { max: {
handler: function(newValue, oldValue) { handler: function(newValue, oldValue) {
this.maxTotal = newValue; this.maxTotal = newValue;
this.size === 'max' && newValue !== oldValue ? this.createData() : null; if(this.size === 'max' && newValue !== oldValue) {
this.createData();
};
}, },
immediate: true, immediate: true,
}, },
min: { min: {
handler: function(newValue, oldValue) { handler: function(newValue, oldValue) {
this.minTotal = newValue; this.minTotal = newValue;
this.size === 'min' && newValue !== oldValue ? this.createData() : null; if( this.size === 'min' && newValue !== oldValue){
this.createData();
}
}, },
immediate: true, immediate: true,
}, },
@@ -192,10 +196,11 @@ export default {
let baseInputValue = event.target.value; let baseInputValue = event.target.value;
let decoratedInputValue; let decoratedInputValue;
// 讓前綴數字自動補 0 // 讓前綴數字自動補 0
isNaN(event.target.value) ? if(isNaN(event.target.value)){
event.target.value = '00' : event.target.value = '00';
// event.target.value = event.target.value.toString().padStart(2, '0'); // 前綴要補 0 } else {
event.target.value = event.target.value.toString(); event.target.value = event.target.value.toString();
}
decoratedInputValue = event.target.value.toString(); decoratedInputValue = event.target.value.toString();
// 手 key 數值大於最大值時,要等於最大值 // 手 key 數值大於最大值時,要等於最大值

View File

@@ -70,7 +70,7 @@ export async function saveFilter(addFilterId, next = null) {
* @param { string } value File's name * @param { string } value File's name
*/ */
export async function savedSuccessfully(value) { export async function savedSuccessfully(value) {
value = value ? value : ''; value = value || '';
await Swal.fire({ await Swal.fire({
title: 'SAVE COMPLETE', title: 'SAVE COMPLETE',
html: `<span class="text-primary">${value}</span> has been saved in Lucia.`, html: `<span class="text-primary">${value}</span> has been saved in Lucia.`,
@@ -132,7 +132,9 @@ export async function leaveFilter(next, addFilterId, toPath, logOut) {
// console.log("PageAdminStore.activePage", PageAdminStore.activePage); // console.log("PageAdminStore.activePage", PageAdminStore.activePage);
pageAdminStore.keepPreviousPage(); pageAdminStore.keepPreviousPage();
logOut ? null : next(false); if(!logOut){
next(false);
};
} }
}; };
@@ -209,7 +211,9 @@ export async function leaveConformance(next, addConformanceCreateCheckId, toPath
conformanceStore.conformanceLogTempCheckId = null; conformanceStore.conformanceLogTempCheckId = null;
logOut ? logOut() : next(toPath); logOut ? logOut() : next(toPath);
} else if(result.dismiss === 'backdrop') { } else if(result.dismiss === 'backdrop') {
logOut ? null : next(false); if(!logOut){
next(false);
}
} }
}; };
/** /**
@@ -262,7 +266,7 @@ export async function uploadFailedSecond(detail) {
detail.forEach(i => { detail.forEach(i => {
let content = ''; let content = '';
let key = '';
switch (i.type) { switch (i.type) {
case 'too_many': case 'too_many':
manySrt = 'There are more errors.'; manySrt = 'There are more errors.';
@@ -274,8 +278,6 @@ export async function uploadFailedSecond(detail) {
content = `<li>Data malformed in Timestamp Column: (Row #${i.loc[1]}, "${i.input}")</li>`; content = `<li>Data malformed in Timestamp Column: (Row #${i.loc[1]}, "${i.input}")</li>`;
break; break;
case 'missing': case 'missing':
let key = '';
switch (i.loc[2]) { switch (i.loc[2]) {
case 'case id': case 'case id':
key = 'Case ID'; key = 'Case ID';
@@ -403,8 +405,7 @@ export async function renameModal(rename, type, id, baseName) {
// 改名成功 // 改名成功
if(isConfirmed) await rename(type, id, value); if(isConfirmed) await rename(type, id, value);
// 清空欄位 // 清空欄位 fileName = '';
fileName = '';
} }
/** /**
* Delete File * Delete File
@@ -464,7 +465,6 @@ export async function reallyDeldetInformation(files, reallyDeldetData) {
const filesStore = FilesStore(); const filesStore = FilesStore();
const deleteCustomClass = { ...customClass }; const deleteCustomClass = { ...customClass };
const htmlText = `<div class="text-left mx-4 space-y-1"><p>The following file(s) have been deleted by other user(s):</p><ul class="list-disc ml-6">${files}</ul></div>`; const htmlText = `<div class="text-left mx-4 space-y-1"><p>The following file(s) have been deleted by other user(s):</p><ul class="list-disc ml-6">${files}</ul></div>`;
let recordIdData = [];
deleteCustomClass.confirmButton = '!inline-block !rounded-full !text-sm !font-medium !text-center !align-middle !transition-colors !duration-300 !px-5 !py-2 !w-[100px] !h-[40px] !text-primary !bg-neutral-10 !border !border-primary'; deleteCustomClass.confirmButton = '!inline-block !rounded-full !text-sm !font-medium !text-center !align-middle !transition-colors !duration-300 !px-5 !py-2 !w-[100px] !h-[40px] !text-primary !bg-neutral-10 !border !border-primary';
deleteCustomClass.cancelButton = null; deleteCustomClass.cancelButton = null;
@@ -481,7 +481,7 @@ export async function reallyDeldetInformation(files, reallyDeldetData) {
confirmButton.style.border = '1px solid #0099FF'; confirmButton.style.border = '1px solid #0099FF';
} }
}); });
recordIdData = await Promise.all(reallyDeldetData.map(file => filesStore.deletionRecord(file.id))); await Promise.all(reallyDeldetData.map(file => filesStore.deletionRecord(file.id)));
await filesStore.fetchAllFiles(); await filesStore.fetchAllFiles();
} }

View File

@@ -113,10 +113,17 @@ export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, cu
// 可使用 parseInt(整數) parseFloat(浮點數) 將字串轉為數字 // 可使用 parseInt(整數) parseFloat(浮點數) 將字串轉為數字
// Relative 要轉為百分比 % // Relative 要轉為百分比 %
if(node.data('type') === 'activity') { if(node.data('type') === 'activity') {
let textInt;
let textFloat;
let textDurRel;
let timeLabelInt;
let timeLabelFloat;
let textTimeLabel;
switch(dataLayerType) { switch(dataLayerType) {
case 'freq': // Frequency case 'freq': // Frequency
let textInt = dataLayerOption === 'rel_freq' ? text + optionValue * 100 + "%" : text + optionValue; textInt = dataLayerOption === 'rel_freq' ? text + optionValue * 100 + "%" : text + optionValue;
let textFloat = dataLayerOption === 'rel_freq'? text + (optionValue * 100).toFixed(2) + "%" : text + optionValue.toFixed(2); textFloat = dataLayerOption === 'rel_freq'? text + (optionValue * 100).toFixed(2) + "%" : text + optionValue.toFixed(2);
// 判斷是否為整數,若非整數要取小數點後面兩個值。 // 判斷是否為整數,若非整數要取小數點後面兩個值。
text = Math.trunc(optionValue) === optionValue ? textInt : textFloat; text = Math.trunc(optionValue) === optionValue ? textInt : textFloat;
@@ -124,13 +131,13 @@ export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, cu
case 'duration': // Duration 除了 Relative 為百分比 % ,其他要轉變時間單位。 case 'duration': // Duration 除了 Relative 為百分比 % ,其他要轉變時間單位。
// Relative % // Relative %
let textDurRel = text + (optionValue * 100).toFixed(2) + "%"; textDurRel = text + (optionValue * 100).toFixed(2) + "%";
// Timelabel // Timelabel
let timeLabelInt = text + getTimeLabel(optionValue); timeLabelInt = text + getTimeLabel(optionValue);
let timeLabelFloat = text + getTimeLabel(optionValue.toFixed(2)); timeLabelFloat = text + getTimeLabel(optionValue.toFixed(2));
// 判斷是否為整數,若非整數要取小數點後面兩個值。 // 判斷是否為整數,若非整數要取小數點後面兩個值。
let textTimeLabel = Math.trunc(optionValue) === optionValue ? timeLabelInt : timeLabelFloat; textTimeLabel = Math.trunc(optionValue) === optionValue ? timeLabelInt : timeLabelFloat;
text = dataLayerOption === 'rel_duration' ? textDurRel : textTimeLabel; text = dataLayerOption === 'rel_duration' ? textDurRel : textTimeLabel;
break; break;
@@ -169,13 +176,18 @@ export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, cu
'content': function(edge) { // 關係線顯示的文字 'content': function(edge) { // 關係線顯示的文字
let optionValue = edge.data(`${dataLayerType}.${dataLayerOption}`); let optionValue = edge.data(`${dataLayerType}.${dataLayerOption}`);
let result = ''; let result = '';
let edgeInt;
let edgeFloat;
let edgeDurRel;
let timeLabelInt;
let timeLabelFloat;
let edgeTimeLabel;
if(optionValue === '') return optionValue; if(optionValue === '') return optionValue;
switch(dataLayerType) { switch(dataLayerType) {
case 'freq': case 'freq':
let edgeInt = dataLayerOption === 'rel_freq' ? optionValue * 100 + "%" : optionValue; edgeInt = dataLayerOption === 'rel_freq' ? optionValue * 100 + "%" : optionValue;
let edgeFloat = dataLayerOption === 'rel_freq' ? (optionValue * 100).toFixed(2) + "%" : optionValue.toFixed(2); edgeFloat = dataLayerOption === 'rel_freq' ? (optionValue * 100).toFixed(2) + "%" : optionValue.toFixed(2);
// 判斷是否為整數,若非整數要取小數點後面兩個值。 // 判斷是否為整數,若非整數要取小數點後面兩個值。
result = Math.trunc(optionValue) === optionValue ? edgeInt : edgeFloat; result = Math.trunc(optionValue) === optionValue ? edgeInt : edgeFloat;
@@ -183,11 +195,11 @@ export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, cu
case 'duration': // Duration 除了 Relative 為百分比 % ,其他要轉變時間單位。 case 'duration': // Duration 除了 Relative 為百分比 % ,其他要轉變時間單位。
// Relative % // Relative %
let edgeDurRel = (optionValue * 100).toFixed(2) + "%"; edgeDurRel = (optionValue * 100).toFixed(2) + "%";
// Timelabel // Timelabel
let timeLabelInt = getTimeLabel(optionValue); timeLabelInt = getTimeLabel(optionValue);
let timeLabelFloat = getTimeLabel(optionValue.toFixed(2)); timeLabelFloat = getTimeLabel(optionValue.toFixed(2));
let edgeTimeLabel = Math.trunc(optionValue) === optionValue ? timeLabelInt : timeLabelFloat; edgeTimeLabel = Math.trunc(optionValue) === optionValue ? timeLabelInt : timeLabelFloat;
result = dataLayerOption === 'rel_duration' ? edgeDurRel : edgeTimeLabel; result = dataLayerOption === 'rel_duration' ? edgeDurRel : edgeTimeLabel;
break; break;
@@ -210,7 +222,7 @@ export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, cu
// creat tippy.js // creat tippy.js
let tip; let tip;
cy.on('mouseover', 'node', function(event) { cy.on('mouseover', 'node', function(event) {
var node = event.target const node = event.target;
let ref = node.popperRef() let ref = node.popperRef()
let dummyDomEle = document.createElement('div'); let dummyDomEle = document.createElement('div');
let content = document.createElement('div'); let content = document.createElement('div');

View File

@@ -86,7 +86,7 @@ export default function cytoscapeMapTrace(nodes, edges, graphId) {
// creat tippy.js // creat tippy.js
let tip; let tip;
cy.on('mouseover', 'node', function(event) { cy.on('mouseover', 'node', function(event) {
var node = event.target const node = event.target
let ref = node.popperRef() let ref = node.popperRef()
let dummyDomEle = document.createElement('div'); let dummyDomEle = document.createElement('div');
let content = document.createElement('div'); let content = document.createElement('div');
@@ -97,7 +97,6 @@ export default function cytoscapeMapTrace(nodes, edges, graphId) {
content:content content:content
}); });
tip.show(); tip.show();
// if(node.data("label").length > 18) tip.show();
}) })
cy.on('mouseout', 'node', function(event) { cy.on('mouseout', 'node', function(event) {
tip.hide(); tip.hide();

View File

@@ -5,7 +5,7 @@
* @returns * @returns
*/ */
export default function numberLabel(num) { export default function numberLabel(num) {
var parts = num.toString().split('.'); let parts = num.toString().split('.');
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ','); parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return parts.join('.'); return parts.join('.');
} }

View File

@@ -177,7 +177,6 @@ export function yTimeRange(data, yAmount, yMax) {
* @returns {numver} x index * @returns {numver} x index
*/ */
export function getXIndex(data, xValue) { export function getXIndex(data, xValue) {
let closestElement = data[0]; // 假定第一个元素是最接近的
let closestIndex = xValue; // 假定第一个元素的索引是 0 let closestIndex = xValue; // 假定第一个元素的索引是 0
let smallestDifference = Math.abs(xValue - data[0]); // 初始差值设为第一个元素与目标数的差值 let smallestDifference = Math.abs(xValue - data[0]); // 初始差值设为第一个元素与目标数的差值
@@ -185,7 +184,6 @@ export function getXIndex(data, xValue) {
let difference = Math.abs(xValue - data[i]); let difference = Math.abs(xValue - data[i]);
if (difference <= smallestDifference) { if (difference <= smallestDifference) {
closestElement = data[i];
closestIndex = i; closestIndex = i;
smallestDifference = difference; smallestDifference = difference;
} }

View File

@@ -6,8 +6,8 @@
export function sortNumEngZhtw(data) { export function sortNumEngZhtw(data) {
return data.sort((a, b) => { return data.sort((a, b) => {
// 檢查兩個值是否都是數字 // 檢查兩個值是否都是數字
var isANumber = !isNaN(parseFloat(a)) && isFinite(a); const isANumber = !isNaN(parseFloat(a)) && isFinite(a);
var isBNumber = !isNaN(parseFloat(b)) && isFinite(b); const isBNumber = !isNaN(parseFloat(b)) && isFinite(b);
// 如果兩個值都是數字,直接比較大小 // 如果兩個值都是數字,直接比較大小
if (isANumber && isBNumber) return parseFloat(a) - parseFloat(b); if (isANumber && isBNumber) return parseFloat(a) - parseFloat(b);
@@ -28,8 +28,8 @@ export function sortNumEngZhtw(data) {
*/ */
export function sortNumEngZhtwForFilter(a, b) { export function sortNumEngZhtwForFilter(a, b) {
// 檢查兩個值是否都是數字 // 檢查兩個值是否都是數字
var isANumber = !isNaN(parseFloat(a)) && isFinite(a); const isANumber = !isNaN(parseFloat(a)) && isFinite(a);
var isBNumber = !isNaN(parseFloat(b)) && isFinite(b); const isBNumber = !isNaN(parseFloat(b)) && isFinite(b);
// 如果兩個值都是數字,直接比較大小 // 如果兩個值都是數字,直接比較大小
if (isANumber && isBNumber) return parseFloat(a) - parseFloat(b); if (isANumber && isBNumber) return parseFloat(a) - parseFloat(b);

View File

@@ -125,9 +125,10 @@ export default defineStore('conformanceStore', {
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');
c.facets.map(fac => { c.facets.map(fac => {
switch(fac.type) { switch(fac.type) {
case 'dummy': //sonar-qube
case 'duration-list': case 'duration-list':
fac.value = fac.value.map(v => v !== null ? abbreviateNumber(new Decimal(v.toFixed(2))) : null); fac.value = fac.value.map(v => v !== null ? abbreviateNumber(new Decimal(v.toFixed(2))) : null);
fac.value = (fac.value).map(v => v = v.trim()).join(', '); fac.value = (fac.value).map(v => v.trim()).join(', ');
break; break;
default: default:
break; break;

View File

@@ -214,7 +214,7 @@ export default defineStore('filesStore', {
break; break;
} }
try { try {
const response = await axios.put(api, data); await axios.put(api, data);
this.uploadFileName = null; this.uploadFileName = null;
await this.fetchAllFiles(); await this.fetchAllFiles();
} catch(error) { } catch(error) {
@@ -277,7 +277,6 @@ export default defineStore('filesStore', {
break; break;
} }
try { try {
const response = await axios.delete(api);
await this.fetchAllFiles(); await this.fetchAllFiles();
await deleteSuccess(); await deleteSuccess();
} catch(error) { } catch(error) {
@@ -296,7 +295,6 @@ export default defineStore('filesStore', {
loading.isLoading = true; loading.isLoading = true;
api = `/api/deletion/${id}`; api = `/api/deletion/${id}`;
try { try {
const response = await axios.delete(api);
} catch(error) { } catch(error) {
apiError(error, 'Failed to Remove a Deletion Record.') apiError(error, 'Failed to Remove a Deletion Record.')
} finally { } finally {

View File

@@ -441,7 +441,7 @@ console.log("TODO:", datasetsPrimary, )
let yDataPrimary; let yDataPrimary;
let labelPrimary = chartData.data[0].label; let labelPrimary = chartData.data[0].label;
let datasetsSecondary = chartData.data[1].data; let datasetsSecondary = chartData.data[1].data;
let xDataSecondary; // let xDataSecondary;
let yDataSecondary; let yDataSecondary;
let labelSecondary = chartData.data[1].label; let labelSecondary = chartData.data[1].label;
let primeVueSetData = {}; let primeVueSetData = {};
@@ -462,7 +462,7 @@ console.log("TODO:", datasetsPrimary, )
y: value.y === null ? null : value.y * 100 y: value.y === null ? null : value.y * 100
} }
}); });
xDataSecondary = datasetsSecondary.map(i => i.x); // xDataSecondary = datasetsSecondary.map(i => i.x);
yDataSecondary = datasetsSecondary.map(i => i.y); yDataSecondary = datasetsSecondary.map(i => i.y);
primeVueSetData = { primeVueSetData = {

View File

@@ -99,7 +99,7 @@ export default {
// .*$:匹配剩餘的字符,確保完整的提取。 // .*$:匹配剩餘的字符,確保完整的提取。
// |^.*$:在找不到 "luciaToken" 的情況下,匹配整個字符串。 // |^.*$:在找不到 "luciaToken" 的情況下,匹配整個字符串。
// 實際應用 // 實際應用
const token = document.cookie.replace(/(?:(?:^|.*;\s*)luciaToken\s*\=\s*([^;]*).*$)|^.*$/, "$1"); const token = document.cookie.replace(/(?:(?:^|.*;\s*)luciaToken\s*=\s*([^;]*).*$)|^.*$/, "$1");
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`; axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
switch (to.params.type) { switch (to.params.type) {

View File

@@ -468,7 +468,7 @@ export default {
if (isCheckPage) { if (isCheckPage) {
const conformanceStore = ConformanceStore(); const conformanceStore = ConformanceStore();
// Save token in Headers. // Save token in Headers.
const token = document.cookie.replace(/(?:(?:^|.*;\s*)luciaToken\s*\=\s*([^;]*).*$)|^.*$/, "$1"); const token = document.cookie.replace(/(?:(?:^|.*;\s*)luciaToken\s*=\s*([^;]*).*$)|^.*$/, "$1");
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`; axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
switch (to.params.type) { switch (to.params.type) {

View File

@@ -267,8 +267,6 @@ export default {
* @param { object } content titels 標題文字 * @param { object } content titels 標題文字
*/ */
getBarChart(chartData, content) { getBarChart(chartData, content) {
const maxX = chartData.x_axis.max;
const minX = chartData.x_axis.min;
const getMoment = (time)=> this.$moment(time).format('YYYY/M/D hh:mm:ss'); const getMoment = (time)=> this.$moment(time).format('YYYY/M/D hh:mm:ss');
let datasets = chartData.data; let datasets = chartData.data;
let xData; let xData;
@@ -914,7 +912,7 @@ export default {
if (isCheckPage) { if (isCheckPage) {
const conformanceStore = ConformanceStore(); const conformanceStore = ConformanceStore();
// Save token in Headers. // Save token in Headers.
const token = document.cookie.replace(/(?:(?:^|.*;\s*)luciaToken\s*\=\s*([^;]*).*$)|^.*$/, "$1"); const token = document.cookie.replace(/(?:(?:^|.*;\s*)luciaToken\s*=\s*([^;]*).*$)|^.*$/, "$1");
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`; axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
switch (to.params.type) { switch (to.params.type) {

View File

@@ -116,9 +116,12 @@
</ul> </ul>
</div> </div>
<!-- All Files type of grid --> <!-- All Files type of grid -->
<draggable tag="ul" :list="compareData" :group="{ name: 'files' }" itemKey="name" class="flex justify-start items-start gap-4 flex-wrap overflow-y-scroll overflow-x-hidden max-h-[calc(100vh_-_440px)] scrollbar" id="compareGridCards"> <ul>
<draggable tag="ul" :list="compareData" :group="{ name: 'files' }" itemKey="name" class="flex justify-start items-start gap-4
flex-wrap overflow-y-scroll overflow-x-hidden max-h-[calc(100vh_-_440px)] scrollbar" id="compareGridCards">
<template #item="{ element, index }"> <template #item="{ element, index }">
<li class="w-[216px] h-[168px] p-4 border rounded border-neutral-300 hover:bg-primary/10 hover:border-primary duration-300 flex flex-col justify-between cursor-pointer" :title="element.name" :id="'compareFile' + index"> <li class="w-[216px] h-[168px] p-4 border rounded border-neutral-300 hover:bg-primary/10 hover:border-primary duration-300
flex flex-col justify-between cursor-pointer" :title="element.name" :id="'compareFile' + index">
<div> <div>
<span class="material-symbols-outlined mb-2 !text-[32px] block"> <span class="material-symbols-outlined mb-2 !text-[32px] block">
{{ element.icon }} {{ element.icon }}
@@ -136,6 +139,7 @@
</li> </li>
</template> </template>
</draggable> </draggable>
</ul>
</section> </section>
<!-- All & Discover --> <!-- All & Discover -->
<section v-else> <section v-else>
@@ -320,7 +324,7 @@
* Compare Submit button disabled * Compare Submit button disabled
*/ */
isCompareDisabledButton: function() { isCompareDisabledButton: function() {
let result = this.primaryDragData.length === 0 || this.secondaryDragData.length === 0 ? true : false; let result = this.primaryDragData.length === 0 || this.secondaryDragData.length === 0;
return result; return result;
}, },
/** /**
@@ -398,17 +402,14 @@
this.$router.push({name: 'Map', params: params}); this.$router.push({name: 'Map', params: params});
break; break;
case 'log-check': case 'log-check':
fileId = file.id;
type = file.parent.type;
params = { type: type, fileId: fileId };
this.$router.push({name: 'CheckConformance', params: params});
break
case 'filter-check': case 'filter-check':
fileId = file.id; fileId = file.id;
type = file.parent.type; type = file.parent.type;
params = { type: type, fileId: fileId }; params = { type: type, fileId: fileId };
this.$router.push({name: 'CheckConformance', params: params}); this.$router.push({name: 'CheckConformance', params: params});
break; break;
default:
break;
} }
}, },
/** /**
@@ -482,10 +483,11 @@
switch (i.type) { switch (i.type) {
case 'log-check': case 'log-check':
i.type = 'rule'; i.type = 'rule';
break;
case 'filter-check': case 'filter-check':
i.type = 'rule'; i.type = 'rule';
break; break;
default:
break;
} }
let content = `<li>[${i.type}] ${i.name}</li>`; let content = `<li>[${i.type}] ${i.name}</li>`;
srt += content; srt += content;
@@ -504,9 +506,8 @@
this.reallyDeldetData.forEach(file => { this.reallyDeldetData.forEach(file => {
switch (file.type) { switch (file.type) {
case 'log-check': case 'log-check':
file.type = 'rule';
break;
case 'filter-check': case 'filter-check':
default:
file.type = 'rule'; file.type = 'rule';
break; break;
} }

View File

@@ -87,7 +87,7 @@ export default {
}, },
created() { created() {
// Save token in Headers. // Save token in Headers.
const token = document.cookie.replace(/(?:(?:^|.*;\s*)luciaToken\s*\=\s*([^;]*).*$)|^.*$/, "$1"); const token = document.cookie.replace(/(?:(?:^|.*;\s*)luciaToken\s*=\s*([^;]*).*$)|^.*$/, "$1");
this.$http.defaults.headers.common['Authorization'] = `Bearer ${token}`; this.$http.defaults.headers.common['Authorization'] = `Bearer ${token}`;
}, },
// 重新整理畫面以及第一次進入網頁時beforeRouteEnter這個hook會被執行然而beforeRouteUpdate不會被執行 // 重新整理畫面以及第一次進入網頁時beforeRouteEnter這個hook會被執行然而beforeRouteUpdate不會被執行

View File

@@ -34,7 +34,9 @@
<!-- Upload table --> <!-- Upload table -->
<div class="overflow-y-auto overflow-x-auto scrollbar max-h-[calc(100%_-_94px)]"> <div class="overflow-y-auto overflow-x-auto scrollbar max-h-[calc(100%_-_94px)]">
<table class="text-sm border-separate border-spacing-0 h-full overflow-y-auto overflow-x-auto scrollbar"> <table class="text-sm border-separate border-spacing-0 h-full overflow-y-auto overflow-x-auto scrollbar">
<caption class="hidden">Upload</caption>
<thead class="sticky top-0 bg-neutral-10"> <thead class="sticky top-0 bg-neutral-10">
<tr class="hidden"><th></th></tr>
<tr> <tr>
<td v-for="(item, index) in uploadDetail?.columns" :key="index" class="border border-neutral-500 p-2 truncate max-w-[198px]">{{ item }}</td> <td v-for="(item, index) in uploadDetail?.columns" :key="index" class="border border-neutral-500 p-2 truncate max-w-[198px]">{{ item }}</td>
</tr> </tr>
@@ -125,7 +127,8 @@ export default {
// 1. 長度一樣,強制每一個都要選 // 1. 長度一樣,強制每一個都要選
// 2. 不為 null undefind // 2. 不為 null undefind
let hasValue = !this.selectedColumns.includes(undefined); let hasValue = !this.selectedColumns.includes(undefined);
let result = !(this.selectedColumns.length === this.uploadDetail?.columns.length && this.informData.length === 0 && this.repeatedData.length === 0 && hasValue) ? true : false; let result = !(this.selectedColumns.length === this.uploadDetail?.columns.length
&& this.informData.length === 0 && this.repeatedData.length === 0 && hasValue);
return result return result
}, },
}, },
@@ -208,7 +211,9 @@ export default {
if(!code || code === 'case_attributes') return; if(!code || code === 'case_attributes') return;
nameOccurrences[name]++; nameOccurrences[name]++;
// 重複的選項只出現一次 // 重複的選項只出現一次
nameOccurrences[name] === 2 ? noSortedRepeatedData.push(item) : false; if(nameOccurrences[name] === 2){
noSortedRepeatedData.push(item)
}
// 要按照選單的順序排序 // 要按照選單的順序排序
this.repeatedData = this.columnType.filter(column => noSortedRepeatedData.includes(column)); this.repeatedData = this.columnType.filter(column => noSortedRepeatedData.includes(column));
}else { }else {
@@ -318,7 +323,6 @@ export default {
if(vm.uploadId == null) { if(vm.uploadId == null) {
vm.$router.push({name: 'Files', replace: true}); vm.$router.push({name: 'Files', replace: true});
vm.$toast.default('Please upload your file.', {position: 'bottom'}); vm.$toast.default('Please upload your file.', {position: 'bottom'});
return
} }
}) })
}, },

View File

@@ -59,8 +59,8 @@ module.exports = {
}, },
'fadeout': { 'fadeout': {
'0%': { opacity: '1' }, '0%': { opacity: '1' },
'0%': { opacity: '0.5' }, '50%': { opacity: '0.5' },
'0%': { opacity: '0' }, '100%': { opacity: '0' },
}, },
'edit': { 'edit': {
'0%, 100%': { transform: 'rotate(0deg)' }, '0%, 100%': { transform: 'rotate(0deg)' },