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 />
</template>
<style scoped></style>
<script setup lang="ts">
import { RouterView } from "vue-router";
</script>

View File

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

View File

@@ -38,13 +38,6 @@
</p>
<Chart type="bar" :data="casesChartData" :options="casesChartOptions" class="w-[99%]"/>
</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>
<!-- effect -->
<section>
@@ -104,6 +97,16 @@
<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>
<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>
<tr v-for="(trace, key) in data.loops" :key="key">
<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'">
<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">
<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>
<tr v-for="(trace, key) in data.issues" :key="key">
<td class="p-2 pl-6 truncate max-w-0 w-1/3">
@@ -500,8 +513,9 @@ export default {
color: '#334155',
align: 'inner',
callback: function(value, index, values) {
if (value === 0) return `${value * 100}%`;
else if (value === 1) return `${value * 100}%`;
if (value === 0 || value === 1) {
return `${value * 100}%`;
}
},
},
grid: {
@@ -585,8 +599,9 @@ export default {
color: '#334155',
align: 'inner',
callback: function(value, index, values) {
if (index === 0) return shortScaleNumber(value);
else if (index === values.length - 1) return shortScaleNumber(value);
if (index === 0 || index === values.length - 1) {
return shortScaleNumber(value);
}
},
},
grid: {
@@ -615,7 +630,7 @@ export default {
let max = yMax * 1.1;
let xVal = timeRange(xMin, xMax, 100);
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));
formattedXVal = formatMaxTwo(formattedXVal);
let selectTimeMinIndex = getXIndex(xVal, this.selectDurationTime.min);

View File

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

View File

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

View File

@@ -288,7 +288,11 @@ export default {
*/
setStartAndEndData(data, category, task) {
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]);
newData = [...new Set(newData)];
return newData;
@@ -361,8 +365,6 @@ export default {
this.task = this.isSubmitShowDataSeq.task;
this.isStartSelected = this.isSubmitShowDataSeq.isStartSelected;
this.isEndSelected = this.isSubmitShowDataSeq.isEndSelected;
// this.taskStart = this.isSubmitShowDataSeq.taskStart;
// this.taskEnd = this.isSubmitShowDataSeq.taskEnd;
break;
case 'Processing time':
switch (this.selectedProcessScope) {
@@ -370,45 +372,36 @@ export default {
this.task = this.isSubmitShowDataPtEte.task;
this.isStartSelected = this.isSubmitShowDataPtEte.isStartSelected;
this.isEndSelected = this.isSubmitShowDataPtEte.isEndSelected;
// this.taskStart = this.isSubmitShowDataPtEte.taskStart;
// this.taskEnd = this.isSubmitShowDataPtEte.taskEnd;
break;
case 'Partial':
this.task = this.isSubmitShowDataPtP.task;
this.isStartSelected = this.isSubmitShowDataPtP.isStartSelected;
this.isEndSelected = this.isSubmitShowDataPtP.isEndSelected;
// this.taskStart = this.isSubmitShowDataPtP.taskStart;
// this.taskEnd = this.isSubmitShowDataPtP.taskEnd;
break;
default:
break;
}
break;
case 'Waiting time':
switch (this.selectedProcessScope) {
case 'End to end':
this.task = this.isSubmitShowDataWtEte.task;
this.isStartSelected = this.isSubmitShowDataWtEte.isStartSelected;
this.isEndSelected = this.isSubmitShowDataWtEte.isEndSelected;
// this.taskStart = this.isSubmitShowDataWtEte.taskStart;
// this.taskEnd = this.isSubmitShowDataWtEte.taskEnd;
break;
case 'Partial':
this.task = this.isSubmitShowDataWtP.task;
this.isStartSelected = this.isSubmitShowDataWtP.isStartSelected;
this.isEndSelected = this.isSubmitShowDataWtP.isEndSelected;
// this.taskStart = this.isSubmitShowDataWtP.taskStart;
// this.taskEnd = this.isSubmitShowDataWtP.taskEnd;
break;
default:
break;
}
switch (this.selectedProcessScope) {
case 'End to end':
this.task = this.isSubmitShowDataWtEte.task;
this.isStartSelected = this.isSubmitShowDataWtEte.isStartSelected;
this.isEndSelected = this.isSubmitShowDataWtEte.isEndSelected;
break;
case 'Partial':
this.task = this.isSubmitShowDataWtP.task;
this.isStartSelected = this.isSubmitShowDataWtP.isStartSelected;
this.isEndSelected = this.isSubmitShowDataWtP.isEndSelected;
break;
default:
break;
}
break;
case 'Cycle time':
this.task = this.isSubmitShowDataCt.task;
this.isStartSelected = this.isSubmitShowDataCt.isStartSelected;
this.isEndSelected = this.isSubmitShowDataCt.isEndSelected;
// this.taskStart = this.isSubmitShowDataCt.taskStart;
// this.taskEnd = this.isSubmitShowDataCt.taskEnd;
break;
default:
break;

View File

@@ -14,6 +14,7 @@
</p>
<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">
<caption class="hidden">Trace List</caption>
<thead class="sticky top-0 z-10 bg-neutral-100">
<tr>
<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
}
: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) {
@apply border-neutral-500 !border-t-0 text-center
@@ -294,9 +296,6 @@ export default {
:deep(.p-column-header-content) {
@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) {
min-width: 72px;
max-width: 184px;

View File

@@ -7,6 +7,7 @@
<!-- 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 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">
<tr>
<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() {
// Activity List 要排序
this.filteredData = this.filteredData.sort((x, y) => {
y.occurrences - x.occurrences
if(y.occurrences === x.occurrences) sortNumEngZhtwForFilter(x.label, y.label);
const diff = y.occurrences - x.occurrences;
return diff !== 0 ? diff : sortNumEngZhtwForFilter(x.label, y.label);
});
return this.filteredData;
}
@@ -147,7 +148,9 @@ export default {
// 拖曳結束要顯示箭頭,但最後一個不用
const lastChild = evt.item.lastChild;
const listIndex = this.listSequence.length - 1
evt.oldIndex !== listIndex ? lastChild.style.display = '' : null;
if (evt.oldIndex !== listIndex) {
lastChild.style.display = '';
}
// reset: 拖曳最後一個元素時,倒數第二的元素的箭頭要隱藏
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="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)]">

View File

@@ -2,8 +2,6 @@
<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)]">

View File

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

View File

@@ -50,7 +50,7 @@ export default {
const { isLoading } = storeToRefs(loadingStore);
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: {
/**

View File

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

View File

@@ -21,6 +21,7 @@
</p>
<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">
<caption class="hidden">Trace list</caption>
<thead class="sticky top-0 z-10 bg-neutral-10">
<tr>
<th class="h2 px-2 border-b border-neutral-500">Trace</th>
@@ -161,7 +162,7 @@ export default {
{ field: 'id', header: 'Case Id' },
{ field: 'started_at', header: 'Start 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
@@ -256,7 +257,6 @@ export default {
async switchCaseData(id, count) {
// 點同一筆 id 不要有動作
if(id == this.showTraceId) return;
// if(count >= 1000) this.isLoading = true; // 超過 1000 筆要 loading 畫面
this.isLoading = true; // 都要 loading 畫面
this.infinit404 = null;
this.infinitMaxItems = false;
@@ -363,14 +363,12 @@ export default {
@apply sticky top-0 left-0 z-10 bg-neutral-10
}
: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) {
@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) {
min-width: 72px;
max-width: 184px;

View File

@@ -38,14 +38,6 @@
<label :for="item + index">{{ item }}</label>
</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>
<p class="h2">Refine</p>
<div v-for="(item, index) in selectFilter['Refine']" :key="index">
@@ -218,60 +210,22 @@ export default {
// Apply Button disabled setting
isDisabledButton: function() {
let disabled = true;
let sele = this.selectValue;
const type = this.selectAttType;
switch(sele[0]) {
case 'Sequence': // Filter Type 選 Sequence 的行為
switch(sele[1]) {
case 'Have activity(s)': // Activity Sequence 選 Have activity(s) 的行為
if(this.selectFilterTask && this.selectFilterTask?.length !== 0) disabled = false;
break;
case 'Start & End': // Activity Sequence 選 Start & End 的行為
switch(sele[2]) {
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;
const { selectValue: sele, selectAttType: type } = this;
const firstSelection = sele[0];
if (firstSelection === 'Sequence') {
disabled = this.handleSequenceSelection(sele);
} else if (firstSelection === 'Attributes') {
disabled = this.handleAttributesSelection(type);
} else if (firstSelection === 'Trace') {
disabled = this.handleTraceSelection();
} else if (firstSelection === 'Timeframes') {
disabled = this.handleTimeframesSelection();
}
this.isDisabled = disabled;
return disabled;
}
},
},
methods: {
/**
@@ -442,60 +396,39 @@ export default {
'occurred-around' : 'active in'
};
switch(e.type){
switch(e.type) { //sonar-qube
case "contains-task":
label = `${includeStr}, ${e.task}`;
type = "Sequence";
break;
case "starts-with":
label = `${includeStr}, start with ${e.task}`;
type = "Sequence";
break;
case "ends-with":
label = `${includeStr}, end with ${e.task}`;
type = "Sequence";
break;
case "start-end":
label = `${includeStr}, start with ${e.starts_with}, end with ${e.ends_with}`;
type = "Sequence";
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;
label = `${includeStr}, ${getTaskLabel(e)}`;
type = "Sequence";
break;
case "start-end":
label = `${includeStr}, start with ${e.starts_with}, end with ${e.ends_with}`;
type = "Sequence";
break;
case "trace-freq":
label = `${includeStr}, from #${e.lower} to #${e.upper}`;
type = "Trace";
break;
label = `${includeStr}, from #${e.lower} to #${e.upper}`;
type = "Trace";
break;
case 'string-attr':
label = `${includeStr}, ${e.key}, ${e.value}`;
type = "Attributes";
break;
case 'boolean-attr':
label = `${includeStr}, ${e.key}, ${this.selectAttribute?.label}`;
type = "Attributes";
break;
case 'int-attr':
case 'float-attr':
label = `${includeStr}, ${e.key}, from ${e.min} to ${e.max}`;
type = "Attributes";
break;
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')}`;
type = "Attributes";
break;
label = `${includeStr}, ${getAttributeLabel(e)}`;
type = "Attributes";
break;
case "occurred-in":
case "started-in":
case "completed-in":
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")} `
type = "Timeframe"
break;
};
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"
break;
};
return {
type,
label,
@@ -528,7 +461,9 @@ export default {
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 發送選取的資料
@@ -537,7 +472,7 @@ export default {
this.isLoading = true;
let data;
let sele = this.selectValue;
let isExclude = sele[5] === 'Exclude' ? true : false;
let isExclude = sele[5] === 'Exclude';
let containmentMap = {
'Contained in': 'occurred-in',
'Started in': 'started-in',
@@ -641,16 +576,17 @@ export default {
break
}
break;
case 'Trace': // Filter Type 選 Trace 的行為
let lowerIndex = this.$refs.filterTraceView.selectArea[0];
let upperIndex = this.$refs.filterTraceView.selectArea[1]-1;
data = {
type: 'trace-freq',
lower: this.allMapDataStore.traces[lowerIndex].id,
upper: this.allMapDataStore.traces[upperIndex].id,
is_exclude: isExclude,
};
break;
case 'Trace': { // Filter Type 選 Trace 的行為
const lowerIndex = this.$refs.filterTraceView.selectArea[0];
const upperIndex = this.$refs.filterTraceView.selectArea[1]-1;
data = {
type: 'trace-freq',
lower: this.allMapDataStore.traces[lowerIndex].id,
upper: this.allMapDataStore.traces[upperIndex].id,
is_exclude: isExclude,
};
break;
}
case 'Timeframes': // Filter Type 選 Timeframes 的行為
data = {
type: containmentMap[sele[6]],
@@ -668,7 +604,10 @@ export default {
await this.allMapDataStore.checkHasResult();
// 有 Data 就加進 Funnel沒有 Data 不加進 Funnel 和跳錯誤訊息
if(this.hasResultRule === null) return this.isLoading = false;
if(this.hasResultRule === null) {
this.isLoading = false;
return;
}
else if(this.hasResultRule) {
if(!this.temporaryData?.length){
this.temporaryData.push(...postData);
@@ -680,13 +619,11 @@ export default {
this.ruleData.push(...postData.map(e => this.setRule(e)))
}
this.reset(false);
// this.isLoading = true;
await new Promise(resolve => setTimeout(resolve, 1000));
this.isLoading = false;
this.$toast.success('Filter applied. Go to Funnel to verify.');
}else {
this.reset(false);
// this.isLoading = true;
await new Promise(resolve => setTimeout(resolve, 1000));
this.isLoading = false;
this.$toast.warning('No result.');
@@ -698,6 +635,78 @@ export default {
sumbitAll() {
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>

View File

@@ -62,6 +62,8 @@
<div class="pt-1 pb-4">
<p class="h2">Case Duration</p>
<table class="text-sm caseDurationTable">
<caption class="hidden">Case Duration</caption>
<th class="hidden"></th>
<tbody>
<tr>
<td>
@@ -189,7 +191,9 @@
</ul>
</TabPanel>
<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">
<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>
@@ -297,9 +301,6 @@ export default {
:deep(.p-tabview-panel) {
@apply animate-fadein
}
/* .caseDurationTable td:first-child {
@apply pr-1
} */
.caseDurationTable td {
@apply scroll-pb-12
}

View File

@@ -12,6 +12,7 @@
</p>
<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">
<caption class="hidden">Trace List</caption>
<thead class="sticky top-0 z-10 bg-neutral-10">
<tr>
<th class="h2 px-2 border-b border-neutral-500">Trace</th>
@@ -125,7 +126,7 @@ export default {
{ field: 'id', header: 'Case Id' },
{ field: 'started_at', header: 'Start 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
@@ -175,7 +176,6 @@ export default {
async switchCaseData(id, count) {
// 點同一筆 id 不要有動作
if(id == this.showTraceId) return;
// if(count >= 1000) this.isLoading = true; // 超過 1000 筆要 loading 畫面
this.isLoading = true; // 都要 loading 畫面
this.infinit404 = null;
this.infinitMaxItems = false;
@@ -287,14 +287,12 @@ export default {
@apply sticky top-0 left-0 z-10 bg-neutral-10
}
: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) {
@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) {
min-width: 72px;
max-width: 184px;

View File

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

View File

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

View File

@@ -75,7 +75,11 @@ export default {
},
},
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>
<!-- Files Page: Search and Upload -->
<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">
Import
<UploadModal :visible="uploadModal" @closeModal="uploadModal = $event"></UploadModal>
@@ -54,7 +38,6 @@
:disabled="disabledSave" @click="saveModal">
Save
</button>
<!-- <button class="btn btn-sm btn-neutral">Download</button> -->
</div>
<AcctMenu/>
</div>
@@ -122,16 +105,16 @@ export default {
case 'Map':
case 'CheckMap':
// 沒有 filter Id, 沒有暫存 tempFilterId Id 就不能存檔
return this.tempFilterId ? false : true;
return !this.tempFilterId;
case 'Conformance':
case 'CheckConformance':
return this.conformanceFilterTempCheckId || this.conformanceLogTempCheckId ? false : true;
return !(this.conformanceFilterTempCheckId || this.conformanceLogTempCheckId);
}
},
showIcon: function() {
let result = true;
result = ['FILES', 'UPLOAD'].includes(this.navViewName) ? false : true;
result = !['FILES', 'UPLOAD'].includes(this.navViewName);
return result;
},
noShowSaveButton: function() {
@@ -157,7 +140,7 @@ export default {
if(this.$route.params.type === 'filter') {
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();
},
methods: {
@@ -251,9 +234,12 @@ export default {
break;
case 'COMPARE':
switch(name) {
case 'dummy':
case 'CompareDashboard':
valueToSet = 'DASHBOARD';
break;
default:
break;
}
break;
}
@@ -274,7 +260,6 @@ export default {
*/
async saveModal() {
// 協助判斷 MAP, CONFORMANCE 儲存有「送出」或「取消」。
let isSaved;
// 傳給 Map通知 Sidebar 要關閉。
this.$emitter.emit('saveModal', false);

View File

@@ -1,7 +1,9 @@
<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">
<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">
<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>

View File

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

View File

@@ -70,7 +70,7 @@ export async function saveFilter(addFilterId, next = null) {
* @param { string } value File's name
*/
export async function savedSuccessfully(value) {
value = value ? value : '';
value = value || '';
await Swal.fire({
title: 'SAVE COMPLETE',
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);
pageAdminStore.keepPreviousPage();
logOut ? null : next(false);
if(!logOut){
next(false);
};
}
};
@@ -209,7 +211,9 @@ export async function leaveConformance(next, addConformanceCreateCheckId, toPath
conformanceStore.conformanceLogTempCheckId = null;
logOut ? logOut() : next(toPath);
} 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 => {
let content = '';
let key = '';
switch (i.type) {
case 'too_many':
manySrt = 'There are more errors.';
@@ -273,9 +277,7 @@ export async function uploadFailedSecond(detail) {
case 'malformed':
content = `<li>Data malformed in Timestamp Column: (Row #${i.loc[1]}, "${i.input}")</li>`;
break;
case 'missing':
let key = '';
case 'missing':
switch (i.loc[2]) {
case 'case id':
key = 'Case ID';
@@ -403,8 +405,7 @@ export async function renameModal(rename, type, id, baseName) {
// 改名成功
if(isConfirmed) await rename(type, id, value);
// 清空欄位
fileName = '';
// 清空欄位 fileName = '';
}
/**
* Delete File
@@ -464,7 +465,6 @@ export async function reallyDeldetInformation(files, reallyDeldetData) {
const filesStore = FilesStore();
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>`;
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.cancelButton = null;
@@ -481,7 +481,7 @@ export async function reallyDeldetInformation(files, reallyDeldetData) {
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();
}

View File

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

View File

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

View File

@@ -5,7 +5,7 @@
* @returns
*/
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, ',');
return parts.join('.');
}

View File

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

View File

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

View File

@@ -125,11 +125,12 @@ export default defineStore('conformanceStore', {
c.completed_at = moment(c.completed_at).format('YYYY/MM/DD HH:mm');
c.facets.map(fac => {
switch(fac.type) {
case 'dummy': //sonar-qube
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 = v.trim()).join(', ');
fac.value = (fac.value).map(v => v.trim()).join(', ');
break;
default:
default:
break;
};
return fac;

View File

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

View File

@@ -441,7 +441,7 @@ console.log("TODO:", datasetsPrimary, )
let yDataPrimary;
let labelPrimary = chartData.data[0].label;
let datasetsSecondary = chartData.data[1].data;
let xDataSecondary;
// let xDataSecondary;
let yDataSecondary;
let labelSecondary = chartData.data[1].label;
let primeVueSetData = {};
@@ -462,7 +462,7 @@ console.log("TODO:", datasetsPrimary, )
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);
primeVueSetData = {

View File

@@ -99,7 +99,7 @@ export default {
// .*$:匹配剩餘的字符,確保完整的提取。
// |^.*$:在找不到 "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}`;
switch (to.params.type) {

View File

@@ -468,7 +468,7 @@ export default {
if (isCheckPage) {
const conformanceStore = ConformanceStore();
// 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}`;
switch (to.params.type) {

View File

@@ -267,8 +267,6 @@ export default {
* @param { object } content titels 標題文字
*/
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');
let datasets = chartData.data;
let xData;
@@ -914,7 +912,7 @@ export default {
if (isCheckPage) {
const conformanceStore = ConformanceStore();
// 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}`;
switch (to.params.type) {

View File

@@ -116,26 +116,30 @@
</ul>
</div>
<!-- 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">
<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">
<div>
<span class="material-symbols-outlined mb-2 !text-[32px] block">
{{ element.icon }}
</span>
<h3 class="text-sm font-medium mb-2 whitespace-nowrap break-keep overflow-hidden text-ellipsis leading-tight">
{{ element.name }}
</h3>
<p class="text-sm text-neutral-500 whitespace-nowrap break-keep text-ellipsis overflow-hidden leading-tight">
{{ element.parentLog }}
<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 }">
<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>
<span class="material-symbols-outlined mb-2 !text-[32px] block">
{{ element.icon }}
</span>
<h3 class="text-sm font-medium mb-2 whitespace-nowrap break-keep overflow-hidden text-ellipsis leading-tight">
{{ element.name }}
</h3>
<p class="text-sm text-neutral-500 whitespace-nowrap break-keep text-ellipsis overflow-hidden leading-tight">
{{ element.parentLog }}
</p>
</div>
<p class="text-sm text-neutral-500">
{{ element.updated_at }}
</p>
</div>
<p class="text-sm text-neutral-500">
{{ element.updated_at }}
</p>
</li>
</template>
</draggable>
</li>
</template>
</draggable>
</ul>
</section>
<!-- All & Discover -->
<section v-else>
@@ -320,7 +324,7 @@
* Compare Submit button disabled
*/
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;
},
/**
@@ -398,17 +402,14 @@
this.$router.push({name: 'Map', params: params});
break;
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':
fileId = file.id;
type = file.parent.type;
params = { type: type, fileId: fileId };
this.$router.push({name: 'CheckConformance', params: params});
break;
default:
break;
}
},
/**
@@ -482,10 +483,11 @@
switch (i.type) {
case 'log-check':
i.type = 'rule';
break;
case 'filter-check':
i.type = 'rule';
break;
default:
break;
}
let content = `<li>[${i.type}] ${i.name}</li>`;
srt += content;
@@ -504,9 +506,8 @@
this.reallyDeldetData.forEach(file => {
switch (file.type) {
case 'log-check':
file.type = 'rule';
break;
case 'filter-check':
default:
file.type = 'rule';
break;
}

View File

@@ -87,7 +87,7 @@ export default {
},
created() {
// 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}`;
},
// 重新整理畫面以及第一次進入網頁時beforeRouteEnter這個hook會被執行然而beforeRouteUpdate不會被執行

View File

@@ -34,7 +34,9 @@
<!-- Upload table -->
<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">
<caption class="hidden">Upload</caption>
<thead class="sticky top-0 bg-neutral-10">
<tr class="hidden"><th></th></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>
</tr>
@@ -125,7 +127,8 @@ export default {
// 1. 長度一樣,強制每一個都要選
// 2. 不為 null undefind
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
},
},
@@ -208,7 +211,9 @@ export default {
if(!code || code === 'case_attributes') return;
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));
}else {
@@ -318,7 +323,6 @@ export default {
if(vm.uploadId == null) {
vm.$router.push({name: 'Files', replace: true});
vm.$toast.default('Please upload your file.', {position: 'bottom'});
return
}
})
},

View File

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