Apply repository-wide ESLint auto-fix formatting pass
Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,30 @@
|
||||
<template>
|
||||
<div class="h-full bg-neutral-10 border border-neutral-300 rounded-xl ml-4 p-4 space-y-2">
|
||||
<div
|
||||
class="h-full bg-neutral-10 border border-neutral-300 rounded-xl ml-4 p-4 space-y-2"
|
||||
>
|
||||
<p class="h2 pl-2 border-b mb-3">Activity list</p>
|
||||
<div class="flex flex-wrap justify-start content-start gap-4 px-2 overflow-y-auto scrollbar h-[calc(100%_-_48px)]" id="cyp-conformance-list-checkbox">
|
||||
<div class="flex items-center w-[166px]" v-for="(act, index) in sortData" :key="index" :title="act">
|
||||
<Checkbox v-model="actList" :inputId="index.toString()" name="actList" :value="act" @change="actListData"/>
|
||||
<label :for="index" class="ml-2 p-2 whitespace-nowrap break-keep text-ellipsis overflow-hidden">{{ act }}</label>
|
||||
<div
|
||||
class="flex flex-wrap justify-start content-start gap-4 px-2 overflow-y-auto scrollbar h-[calc(100%_-_48px)]"
|
||||
id="cyp-conformance-list-checkbox"
|
||||
>
|
||||
<div
|
||||
class="flex items-center w-[166px]"
|
||||
v-for="(act, index) in sortData"
|
||||
:key="index"
|
||||
:title="act"
|
||||
>
|
||||
<Checkbox
|
||||
v-model="actList"
|
||||
:inputId="index.toString()"
|
||||
name="actList"
|
||||
:value="act"
|
||||
@change="actListData"
|
||||
/>
|
||||
<label
|
||||
:for="index"
|
||||
class="ml-2 p-2 whitespace-nowrap break-keep text-ellipsis overflow-hidden"
|
||||
>{{ act }}</label
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -20,30 +40,37 @@
|
||||
* Checkbox-based activity list for conformance checking input.
|
||||
*/
|
||||
|
||||
import { ref, watch } from 'vue';
|
||||
import { sortNumEngZhtw } from '@/module/sortNumEngZhtw.js';
|
||||
import emitter from '@/utils/emitter';
|
||||
import { ref, watch } from "vue";
|
||||
import { sortNumEngZhtw } from "@/module/sortNumEngZhtw.js";
|
||||
import emitter from "@/utils/emitter";
|
||||
|
||||
const props = defineProps(['data', 'select']);
|
||||
const props = defineProps(["data", "select"]);
|
||||
|
||||
const sortData = ref([]);
|
||||
const actList = ref(props.select);
|
||||
|
||||
watch(() => props.data, (newValue) => {
|
||||
sortData.value = sortNumEngZhtw(newValue);
|
||||
}, { immediate: true });
|
||||
watch(
|
||||
() => props.data,
|
||||
(newValue) => {
|
||||
sortData.value = sortNumEngZhtw(newValue);
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
watch(() => props.select, (newValue) => {
|
||||
actList.value = newValue;
|
||||
});
|
||||
watch(
|
||||
() => props.select,
|
||||
(newValue) => {
|
||||
actList.value = newValue;
|
||||
},
|
||||
);
|
||||
|
||||
/** Emits the selected activities list via the event bus. */
|
||||
function actListData() {
|
||||
emitter.emit('actListData', actList.value);
|
||||
emitter.emit("actListData", actList.value);
|
||||
}
|
||||
|
||||
// created
|
||||
emitter.on('reset', (data) => {
|
||||
emitter.on("reset", (data) => {
|
||||
actList.value = data;
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,10 +1,29 @@
|
||||
<template>
|
||||
<div class="h-full bg-neutral-10 border border-neutral-300 rounded-xl ml-4 p-4 space-y-2">
|
||||
<div
|
||||
class="h-full bg-neutral-10 border border-neutral-300 rounded-xl ml-4 p-4 space-y-2"
|
||||
>
|
||||
<p class="h2 pl-2 border-b mb-3">{{ title }}</p>
|
||||
<div class="flex flex-wrap justify-start content-start gap-4 px-2 overflow-y-auto scrollbar h-[calc(100%_-_48px)]">
|
||||
<div class="flex items-center w-[166px]" v-for="(act, index) in sortData" :key="index" :title="act">
|
||||
<RadioButton v-model="selectedRadio" :inputId="index + act" :name="select" :value="act" @change="actRadioData" />
|
||||
<label :for="index + act" class="ml-2 p-2 whitespace-nowrap break-keep text-ellipsis overflow-hidden">{{ act }}</label>
|
||||
<div
|
||||
class="flex flex-wrap justify-start content-start gap-4 px-2 overflow-y-auto scrollbar h-[calc(100%_-_48px)]"
|
||||
>
|
||||
<div
|
||||
class="flex items-center w-[166px]"
|
||||
v-for="(act, index) in sortData"
|
||||
:key="index"
|
||||
:title="act"
|
||||
>
|
||||
<RadioButton
|
||||
v-model="selectedRadio"
|
||||
:inputId="index + act"
|
||||
:name="select"
|
||||
:value="act"
|
||||
@change="actRadioData"
|
||||
/>
|
||||
<label
|
||||
:for="index + act"
|
||||
class="ml-2 p-2 whitespace-nowrap break-keep text-ellipsis overflow-hidden"
|
||||
>{{ act }}</label
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -22,13 +41,20 @@
|
||||
* start/end activity input.
|
||||
*/
|
||||
|
||||
import { ref, computed, watch } from 'vue';
|
||||
import { ref, computed, watch } from "vue";
|
||||
import { useConformanceInputStore } from "@/stores/conformanceInput";
|
||||
import { sortNumEngZhtw } from '@/module/sortNumEngZhtw.js';
|
||||
import emitter from '@/utils/emitter';
|
||||
import { sortNumEngZhtw } from "@/module/sortNumEngZhtw.js";
|
||||
import emitter from "@/utils/emitter";
|
||||
|
||||
const props = defineProps(['title', 'select', 'data', 'category', 'task', 'isSubmit']);
|
||||
const emit = defineEmits(['selected-task']);
|
||||
const props = defineProps([
|
||||
"title",
|
||||
"select",
|
||||
"data",
|
||||
"category",
|
||||
"task",
|
||||
"isSubmit",
|
||||
]);
|
||||
const emit = defineEmits(["selected-task"]);
|
||||
|
||||
const conformanceInputStore = useConformanceInputStore();
|
||||
|
||||
@@ -36,13 +62,20 @@ const sortData = ref([]);
|
||||
const localSelect = ref(null);
|
||||
const selectedRadio = ref(null);
|
||||
|
||||
watch(() => props.data, (newValue) => {
|
||||
sortData.value = sortNumEngZhtw(newValue);
|
||||
}, { immediate: true });
|
||||
watch(
|
||||
() => props.data,
|
||||
(newValue) => {
|
||||
sortData.value = sortNumEngZhtw(newValue);
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
watch(() => props.task, (newValue) => {
|
||||
selectedRadio.value = newValue;
|
||||
});
|
||||
watch(
|
||||
() => props.task,
|
||||
(newValue) => {
|
||||
selectedRadio.value = newValue;
|
||||
},
|
||||
);
|
||||
|
||||
const inputActivityRadioData = computed(() => ({
|
||||
category: props.category,
|
||||
@@ -52,22 +85,27 @@ const inputActivityRadioData = computed(() => ({
|
||||
/** Emits the selected activity via event bus and updates the store. */
|
||||
function actRadioData() {
|
||||
localSelect.value = null;
|
||||
emitter.emit('actRadioData', inputActivityRadioData.value);
|
||||
emit('selected-task', selectedRadio.value);
|
||||
conformanceInputStore.setActivityRadioStartEndData(inputActivityRadioData.value.task);
|
||||
emitter.emit("actRadioData", inputActivityRadioData.value);
|
||||
emit("selected-task", selectedRadio.value);
|
||||
conformanceInputStore.setActivityRadioStartEndData(
|
||||
inputActivityRadioData.value.task,
|
||||
);
|
||||
}
|
||||
|
||||
/** Sets the global activity radio data state in the conformance input store. */
|
||||
function setGlobalActivityRadioDataState() {
|
||||
//this.title: value might be "From" or "To"
|
||||
conformanceInputStore.setActivityRadioStartEndData(inputActivityRadioData.value.task, props.title);
|
||||
conformanceInputStore.setActivityRadioStartEndData(
|
||||
inputActivityRadioData.value.task,
|
||||
props.title,
|
||||
);
|
||||
}
|
||||
|
||||
// created
|
||||
sortNumEngZhtw(sortData.value);
|
||||
localSelect.value = props.isSubmit ? props.select : null;
|
||||
selectedRadio.value = localSelect.value;
|
||||
emitter.on('reset', (data) => {
|
||||
emitter.on("reset", (data) => {
|
||||
selectedRadio.value = data;
|
||||
});
|
||||
setGlobalActivityRadioDataState();
|
||||
|
||||
@@ -1,38 +1,99 @@
|
||||
<template>
|
||||
<div class="h-full w-full flex justify-between items-center">
|
||||
<!-- Activity List -->
|
||||
<div class="h-full w-full bg-neutral-10 border border-neutral-300 rounded-xl ml-4 p-4 space-y-2">
|
||||
<div
|
||||
class="h-full w-full bg-neutral-10 border border-neutral-300 rounded-xl ml-4 p-4 space-y-2"
|
||||
>
|
||||
<p class="h2 pl-2 border-b mb-3">Activity list</p>
|
||||
<div class="h-[calc(100%_-_56px)]">
|
||||
<Draggable :list="datadata" :group="{name: 'activity', pull: 'clone' }" itemKey="name" animation="300" :fallbackTolerance="5" :forceFallback="true" :ghostClass="'ghostSelected'" :dragClass="'dragSelected'" :sort="false" @end="onEnd" class="h-full flex flex-wrap justify-start content-start gap-4 px-2 overflow-y-auto scrollbar">
|
||||
<Draggable
|
||||
:list="datadata"
|
||||
:group="{ name: 'activity', pull: 'clone' }"
|
||||
itemKey="name"
|
||||
animation="300"
|
||||
:fallbackTolerance="5"
|
||||
:forceFallback="true"
|
||||
:ghostClass="'ghostSelected'"
|
||||
:dragClass="'dragSelected'"
|
||||
:sort="false"
|
||||
@end="onEnd"
|
||||
class="h-full flex flex-wrap justify-start content-start gap-4 px-2 overflow-y-auto scrollbar"
|
||||
>
|
||||
<template #item="{ element, index }">
|
||||
<div :class="listSequence.includes(element) ? 'border-primary text-primary' : ''" class="flex items-center w-[166px] border rounded p-2 bg-neutral-10 cursor-pointer hover:bg-primary/20" @dblclick="moveActItem(index, element)" :title="element">
|
||||
<span class="whitespace-nowrap break-keep text-ellipsis overflow-hidden">{{ element }}</span>
|
||||
<div
|
||||
:class="
|
||||
listSequence.includes(element)
|
||||
? 'border-primary text-primary'
|
||||
: ''
|
||||
"
|
||||
class="flex items-center w-[166px] border rounded p-2 bg-neutral-10 cursor-pointer hover:bg-primary/20"
|
||||
@dblclick="moveActItem(index, element)"
|
||||
:title="element"
|
||||
>
|
||||
<span
|
||||
class="whitespace-nowrap break-keep text-ellipsis overflow-hidden"
|
||||
>{{ element }}</span
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
</Draggable>
|
||||
</div>
|
||||
</div>
|
||||
<!-- sequence -->
|
||||
<div class="w-full h-full relative bg-neutral-10 border border-neutral-300 rounded-xl ml-4 p-4 space-y-2 text-sm">
|
||||
<div
|
||||
class="w-full h-full relative bg-neutral-10 border border-neutral-300 rounded-xl ml-4 p-4 space-y-2 text-sm"
|
||||
>
|
||||
<p class="h2 border-b border-500 mb-3">Sequence</p>
|
||||
<!-- No Data -->
|
||||
<div v-if="listSequence && listSequence.length === 0" class="p-4 w-[calc(100%_-_32px)] h-5/6 flex justify-center items-center absolute">
|
||||
<p class="text-neutral-500">Please drag and drop at least two activities here and sort.</p>
|
||||
<div
|
||||
v-if="listSequence && listSequence.length === 0"
|
||||
class="p-4 w-[calc(100%_-_32px)] h-5/6 flex justify-center items-center absolute"
|
||||
>
|
||||
<p class="text-neutral-500">
|
||||
Please drag and drop at least two activities here and sort.
|
||||
</p>
|
||||
</div>
|
||||
<!-- Have Data -->
|
||||
<div class="m-auto w-full h-[calc(100%_-_56px)]">
|
||||
<div class="w-full h-full overflow-y-auto overflow-x-auto scrollbar px-4 text-center">
|
||||
<draggable class="h-full" :group="{name: 'activity'}" :list="listSequence" itemKey="name" animation="300" :forceFallback="true" :dragClass="'dragSelected'" :fallbackTolerance="5" @start="onStart" @end="onEnd" :component-data="getComponentData()" :ghostClass="'!opacity-0'">
|
||||
<div class="m-auto w-full h-[calc(100%_-_56px)]">
|
||||
<div
|
||||
class="w-full h-full overflow-y-auto overflow-x-auto scrollbar px-4 text-center"
|
||||
>
|
||||
<draggable
|
||||
class="h-full"
|
||||
:group="{ name: 'activity' }"
|
||||
:list="listSequence"
|
||||
itemKey="name"
|
||||
animation="300"
|
||||
:forceFallback="true"
|
||||
:dragClass="'dragSelected'"
|
||||
:fallbackTolerance="5"
|
||||
@start="onStart"
|
||||
@end="onEnd"
|
||||
:component-data="getComponentData()"
|
||||
:ghostClass="'!opacity-0'"
|
||||
>
|
||||
<template #item="{ element, index }">
|
||||
<div :title="element">
|
||||
<div class="flex justify-center items-center">
|
||||
<div class="w-full p-2 border rounded bg-neutral-10 cursor-pointer hover:bg-primary/20" @dblclick="moveSeqItem(index, element)">
|
||||
<div
|
||||
class="w-full p-2 border rounded bg-neutral-10 cursor-pointer hover:bg-primary/20"
|
||||
@dblclick="moveSeqItem(index, element)"
|
||||
>
|
||||
<span>{{ element }}</span>
|
||||
</div>
|
||||
<span class="material-symbols-outlined pl-1 cursor-pointer duration-300 hover:text-danger" @click.stop.native="moveSeqItem(index, element)">close</span>
|
||||
<span
|
||||
class="material-symbols-outlined pl-1 cursor-pointer duration-300 hover:text-danger"
|
||||
@click.stop.native="moveSeqItem(index, element)"
|
||||
>close</span
|
||||
>
|
||||
</div>
|
||||
<span v-show="index !== listSequence.length - 1 && index !== lastItemIndex - 1" class="pi pi-chevron-down !text-lg inline-block py-2 pr-7"></span>
|
||||
<span
|
||||
v-show="
|
||||
index !== listSequence.length - 1 &&
|
||||
index !== lastItemIndex - 1
|
||||
"
|
||||
class="pi pi-chevron-down !text-lg inline-block py-2 pr-7"
|
||||
></span>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
@@ -54,11 +115,11 @@
|
||||
* conformance rule configuration.
|
||||
*/
|
||||
|
||||
import { ref, computed } from 'vue';
|
||||
import { sortNumEngZhtw } from '@/module/sortNumEngZhtw.js';
|
||||
import emitter from '@/utils/emitter';
|
||||
import { ref, computed } from "vue";
|
||||
import { sortNumEngZhtw } from "@/module/sortNumEngZhtw.js";
|
||||
import emitter from "@/utils/emitter";
|
||||
|
||||
const props = defineProps(['data', 'listSeq', 'isSubmit', 'category']);
|
||||
const props = defineProps(["data", "listSeq", "isSubmit", "category"]);
|
||||
|
||||
const listSequence = ref([]);
|
||||
const lastItemIndex = ref(null);
|
||||
@@ -67,7 +128,7 @@ const isSelect = ref(true);
|
||||
const datadata = computed(() => {
|
||||
// Sort the Activity List
|
||||
let newData;
|
||||
if(props.data !== null) {
|
||||
if (props.data !== null) {
|
||||
newData = JSON.parse(JSON.stringify(props.data));
|
||||
sortNumEngZhtw(newData);
|
||||
}
|
||||
@@ -96,7 +157,7 @@ function moveSeqItem(index, element) {
|
||||
* get listSequence
|
||||
*/
|
||||
function getComponentData() {
|
||||
emitter.emit('getListSequence', {
|
||||
emitter.emit("getListSequence", {
|
||||
category: props.category,
|
||||
task: listSequence.value,
|
||||
});
|
||||
@@ -107,13 +168,13 @@ function getComponentData() {
|
||||
*/
|
||||
function onStart(evt) {
|
||||
const lastChild = evt.to.lastChild.lastChild;
|
||||
lastChild.style.display = 'none';
|
||||
lastChild.style.display = "none";
|
||||
// Hide the dragged element at its original position
|
||||
const originalElement = evt.item;
|
||||
originalElement.style.display = 'none';
|
||||
originalElement.style.display = "none";
|
||||
// When dragging the last element, hide the arrow of the second-to-last element
|
||||
const listIndex = listSequence.value.length - 1;
|
||||
if(evt.oldIndex === listIndex) lastItemIndex.value = listIndex;
|
||||
if (evt.oldIndex === listIndex) lastItemIndex.value = listIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,12 +183,12 @@ function onStart(evt) {
|
||||
function onEnd(evt) {
|
||||
// Show the dragged element
|
||||
const originalElement = evt.item;
|
||||
originalElement.style.display = '';
|
||||
originalElement.style.display = "";
|
||||
// Show the arrow after drag ends, except for the last element
|
||||
const lastChild = evt.item.lastChild;
|
||||
const listIndex = listSequence.value.length - 1;
|
||||
if (evt.oldIndex !== listIndex) {
|
||||
lastChild.style.display = '';
|
||||
lastChild.style.display = "";
|
||||
}
|
||||
// Reset: hide the second-to-last element's arrow when dragging the last element
|
||||
lastItemIndex.value = null;
|
||||
@@ -136,16 +197,16 @@ function onEnd(evt) {
|
||||
// created
|
||||
const newlist = JSON.parse(JSON.stringify(props.listSeq));
|
||||
listSequence.value = props.isSubmit ? newlist : [];
|
||||
emitter.on('reset', (data) => {
|
||||
emitter.on("reset", (data) => {
|
||||
listSequence.value = [];
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
@reference "../../../../assets/tailwind.css";
|
||||
.ghostSelected {
|
||||
@apply bg-primary/20
|
||||
@apply bg-primary/20;
|
||||
}
|
||||
.dragSelected {
|
||||
@apply !opacity-100
|
||||
@apply !opacity-100;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,50 +1,117 @@
|
||||
<template>
|
||||
<section class="space-y-2 text-sm">
|
||||
<section class="space-y-2 text-sm">
|
||||
<!-- Rule Type -->
|
||||
<div id="cyp-conformance-type-radio">
|
||||
<p class="h2">Rule Type</p>
|
||||
<div v-for="rule in ruleType" :key="rule.id" class="ml-4 mb-2">
|
||||
<RadioButton v-model="selectedRuleType" :inputId="rule.id + rule.name" name="ruleType" :value="rule.name" @change="changeRadio"/>
|
||||
<RadioButton
|
||||
v-model="selectedRuleType"
|
||||
:inputId="rule.id + rule.name"
|
||||
name="ruleType"
|
||||
:value="rule.name"
|
||||
@change="changeRadio"
|
||||
/>
|
||||
<label :for="rule.id + rule.name" class="ml-2">{{ rule.name }}</label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Activity Sequence (2 item) -->
|
||||
<div v-show="selectedRuleType === 'Activity sequence'" id="cyp-conformance-sequence-radio">
|
||||
<div
|
||||
v-show="selectedRuleType === 'Activity sequence'"
|
||||
id="cyp-conformance-sequence-radio"
|
||||
>
|
||||
<p class="h2">Activity Sequence</p>
|
||||
<div v-for="act in activitySequence" :key="act.id" class="ml-4 mb-2">
|
||||
<RadioButton v-model="selectedActivitySequence" :inputId="act.id + act.name" name="activitySequence" :value="act.name" @change="changeRadioSeq"/>
|
||||
<RadioButton
|
||||
v-model="selectedActivitySequence"
|
||||
:inputId="act.id + act.name"
|
||||
name="activitySequence"
|
||||
:value="act.name"
|
||||
@change="changeRadioSeq"
|
||||
/>
|
||||
<label :for="act.id + act.name" class="ml-2">{{ act.name }}</label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Mode -->
|
||||
<div v-show="selectedRuleType === 'Activity sequence' && selectedActivitySequence === 'Sequence'" id="cyp-conformance-Mode-radio">
|
||||
<div
|
||||
v-show="
|
||||
selectedRuleType === 'Activity sequence' &&
|
||||
selectedActivitySequence === 'Sequence'
|
||||
"
|
||||
id="cyp-conformance-Mode-radio"
|
||||
>
|
||||
<p class="h2">Mode</p>
|
||||
<div v-for="mode in mode" :key="mode.id" class="ml-4 mb-2">
|
||||
<RadioButton v-model="selectedMode" :inputId="mode.id + mode.name" name="mode" :value="mode.name" />
|
||||
<RadioButton
|
||||
v-model="selectedMode"
|
||||
:inputId="mode.id + mode.name"
|
||||
name="mode"
|
||||
:value="mode.name"
|
||||
/>
|
||||
<label :for="mode.id + mode.name" class="ml-2">{{ mode.name }}</label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Process Scope -->
|
||||
<div v-show="selectedRuleType === 'Processing time' || selectedRuleType === 'Waiting time'" id="cyp-conformance-procss-radio">
|
||||
<div
|
||||
v-show="
|
||||
selectedRuleType === 'Processing time' ||
|
||||
selectedRuleType === 'Waiting time'
|
||||
"
|
||||
id="cyp-conformance-procss-radio"
|
||||
>
|
||||
<p class="h2">Process Scope</p>
|
||||
<div v-for="pro in processScope" :key="pro.id" class="ml-4 mb-2">
|
||||
<RadioButton v-model="selectedProcessScope" :inputId="pro.id + pro.name" name="processScope" :value="pro.name" @change="changeRadioProcessScope"/>
|
||||
<RadioButton
|
||||
v-model="selectedProcessScope"
|
||||
:inputId="pro.id + pro.name"
|
||||
name="processScope"
|
||||
:value="pro.name"
|
||||
@change="changeRadioProcessScope"
|
||||
/>
|
||||
<label :for="pro.id + pro.name" class="ml-2">{{ pro.name }}</label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Activity Sequence (4 item) -->
|
||||
<div v-show="(selectedRuleType === 'Processing time' && selectedProcessScope === 'End to end') || (selectedRuleType === 'Waiting time' && selectedProcessScope === 'End to end') || selectedRuleType === 'Cycle time'" id="cyp-conformance-actseq-radio">
|
||||
<div
|
||||
v-show="
|
||||
(selectedRuleType === 'Processing time' &&
|
||||
selectedProcessScope === 'End to end') ||
|
||||
(selectedRuleType === 'Waiting time' &&
|
||||
selectedProcessScope === 'End to end') ||
|
||||
selectedRuleType === 'Cycle time'
|
||||
"
|
||||
id="cyp-conformance-actseq-radio"
|
||||
>
|
||||
<p class="h2">Activity Sequence</p>
|
||||
<div v-for="act in actSeqMore" :key="act.id" class="ml-4 mb-2">
|
||||
<RadioButton v-model="selectedActSeqMore" :inputId="act.id + act.name" name="activitySequenceMore" :value="act.name" @change="changeRadioActSeqMore"/>
|
||||
<RadioButton
|
||||
v-model="selectedActSeqMore"
|
||||
:inputId="act.id + act.name"
|
||||
name="activitySequenceMore"
|
||||
:value="act.name"
|
||||
@change="changeRadioActSeqMore"
|
||||
/>
|
||||
<label :for="act.id + act.name" class="ml-2">{{ act.name }}</label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Activity Sequence (3 item) -->
|
||||
<div v-show="(selectedRuleType === 'Processing time' && selectedProcessScope === 'Partial') || (selectedRuleType === 'Waiting time' && selectedProcessScope === 'Partial')" id="cyp-conformance-actseqfromto-radio">
|
||||
<div
|
||||
v-show="
|
||||
(selectedRuleType === 'Processing time' &&
|
||||
selectedProcessScope === 'Partial') ||
|
||||
(selectedRuleType === 'Waiting time' &&
|
||||
selectedProcessScope === 'Partial')
|
||||
"
|
||||
id="cyp-conformance-actseqfromto-radio"
|
||||
>
|
||||
<p class="h2">Activity Sequence</p>
|
||||
<div v-for="act in actSeqFromTo" :key="act.id" class="ml-4 mb-2">
|
||||
<RadioButton v-model="selectedActSeqFromTo" :inputId="act.id + act.name" name="activitySequenceFromTo" :value="act.name" @change="changeRadioActSeqFromTo"/>
|
||||
<RadioButton
|
||||
v-model="selectedActSeqFromTo"
|
||||
:inputId="act.id + act.name"
|
||||
name="activitySequenceFromTo"
|
||||
:value="act.name"
|
||||
@change="changeRadioActSeqFromTo"
|
||||
/>
|
||||
<label :for="act.id + act.name" class="ml-2">{{ act.name }}</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -62,70 +129,77 @@
|
||||
* sequence, mode, and process scope selection.
|
||||
*/
|
||||
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useConformanceStore } from '@/stores/conformance';
|
||||
import emitter from '@/utils/emitter';
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useConformanceStore } from "@/stores/conformance";
|
||||
import emitter from "@/utils/emitter";
|
||||
|
||||
const conformanceStore = useConformanceStore();
|
||||
const { selectedRuleType, selectedActivitySequence, selectedMode, selectedProcessScope, selectedActSeqMore, selectedActSeqFromTo } = storeToRefs(conformanceStore);
|
||||
const {
|
||||
selectedRuleType,
|
||||
selectedActivitySequence,
|
||||
selectedMode,
|
||||
selectedProcessScope,
|
||||
selectedActSeqMore,
|
||||
selectedActSeqFromTo,
|
||||
} = storeToRefs(conformanceStore);
|
||||
|
||||
const ruleType = [
|
||||
{id: 1, name: 'Have activity'},
|
||||
{id: 2, name: 'Activity sequence'},
|
||||
{id: 3, name: 'Activity duration'},
|
||||
{id: 4, name: 'Processing time'},
|
||||
{id: 5, name: 'Waiting time'},
|
||||
{id: 6, name: 'Cycle time'},
|
||||
{ id: 1, name: "Have activity" },
|
||||
{ id: 2, name: "Activity sequence" },
|
||||
{ id: 3, name: "Activity duration" },
|
||||
{ id: 4, name: "Processing time" },
|
||||
{ id: 5, name: "Waiting time" },
|
||||
{ id: 6, name: "Cycle time" },
|
||||
];
|
||||
const activitySequence = [
|
||||
{id: 1, name: 'Start & End'},
|
||||
{id: 2, name: 'Sequence'},
|
||||
{ id: 1, name: "Start & End" },
|
||||
{ id: 2, name: "Sequence" },
|
||||
];
|
||||
const mode = [
|
||||
{id: 1, name: 'Directly follows'},
|
||||
{id: 2, name: 'Eventually follows'},
|
||||
{id: 3, name: 'Short loop(s)'},
|
||||
{id: 4, name: 'Self loop(s)'},
|
||||
{ id: 1, name: "Directly follows" },
|
||||
{ id: 2, name: "Eventually follows" },
|
||||
{ id: 3, name: "Short loop(s)" },
|
||||
{ id: 4, name: "Self loop(s)" },
|
||||
];
|
||||
const processScope = [
|
||||
{id: 1, name: 'End to end'},
|
||||
{id: 2, name: 'Partial'},
|
||||
{ id: 1, name: "End to end" },
|
||||
{ id: 2, name: "Partial" },
|
||||
];
|
||||
const actSeqMore = [
|
||||
{id: 1, name: 'All'},
|
||||
{id: 2, name: 'Start'},
|
||||
{id: 3, name: 'End'},
|
||||
{id: 4, name: 'Start & End'},
|
||||
{ id: 1, name: "All" },
|
||||
{ id: 2, name: "Start" },
|
||||
{ id: 3, name: "End" },
|
||||
{ id: 4, name: "Start & End" },
|
||||
];
|
||||
const actSeqFromTo = [
|
||||
{id: 1, name: 'From'},
|
||||
{id: 2, name: 'To'},
|
||||
{id: 3, name: 'From & To'},
|
||||
{ id: 1, name: "From" },
|
||||
{ id: 2, name: "To" },
|
||||
{ id: 3, name: "From & To" },
|
||||
];
|
||||
|
||||
/** Resets dependent selections when the rule type radio changes. */
|
||||
function changeRadio() {
|
||||
selectedActivitySequence.value = 'Start & End';
|
||||
selectedMode.value = 'Directly follows';
|
||||
selectedProcessScope.value = 'End to end';
|
||||
selectedActSeqMore.value = 'All';
|
||||
selectedActSeqFromTo.value = 'From';
|
||||
emitter.emit('isRadioChange', true); // Clear data when switching radio buttons
|
||||
selectedActivitySequence.value = "Start & End";
|
||||
selectedMode.value = "Directly follows";
|
||||
selectedProcessScope.value = "End to end";
|
||||
selectedActSeqMore.value = "All";
|
||||
selectedActSeqFromTo.value = "From";
|
||||
emitter.emit("isRadioChange", true); // Clear data when switching radio buttons
|
||||
}
|
||||
/** Emits event when the activity sequence radio changes. */
|
||||
function changeRadioSeq() {
|
||||
emitter.emit('isRadioSeqChange',true);
|
||||
emitter.emit("isRadioSeqChange", true);
|
||||
}
|
||||
/** Emits event when the process scope radio changes. */
|
||||
function changeRadioProcessScope() {
|
||||
emitter.emit('isRadioProcessScopeChange', true);
|
||||
emitter.emit("isRadioProcessScopeChange", true);
|
||||
}
|
||||
/** Emits event when the extended activity sequence radio changes. */
|
||||
function changeRadioActSeqMore() {
|
||||
emitter.emit('isRadioActSeqMoreChange', true);
|
||||
emitter.emit("isRadioActSeqMoreChange", true);
|
||||
}
|
||||
/** Emits event when the from/to activity sequence radio changes. */
|
||||
function changeRadioActSeqFromTo() {
|
||||
emitter.emit('isRadioActSeqFromToChange', true);
|
||||
emitter.emit("isRadioActSeqFromToChange", true);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,31 +1,183 @@
|
||||
<template>
|
||||
<div class="px-4 text-sm">
|
||||
<!-- Have activity -->
|
||||
<ResultCheck v-if="selectedRuleType === 'Have activity'" :data="state.containstTasksData" :select="isSubmitTask"></ResultCheck>
|
||||
<ResultCheck
|
||||
v-if="selectedRuleType === 'Have activity'"
|
||||
:data="state.containstTasksData"
|
||||
:select="isSubmitTask"
|
||||
></ResultCheck>
|
||||
<!-- Activity sequence -->
|
||||
<ResultDot v-if="selectedRuleType === 'Activity sequence' && selectedActivitySequence === 'Start & End'" :timeResultData="selectCfmSeqSE" :select="isSubmitStartAndEnd"></ResultDot>
|
||||
<ResultArrow v-if="selectedRuleType === 'Activity sequence' && selectedActivitySequence === 'Sequence' && selectedMode === 'Directly follows'" :data="state.selectCfmSeqDirectly" :select="isSubmitCfmSeqDirectly"></ResultArrow>
|
||||
<ResultArrow v-if="selectedRuleType === 'Activity sequence' && selectedActivitySequence === 'Sequence' && selectedMode === 'Eventually follows'" :data="state.selectCfmSeqEventually" :select="isSubmitCfmSeqEventually"></ResultArrow>
|
||||
<ResultDot
|
||||
v-if="
|
||||
selectedRuleType === 'Activity sequence' &&
|
||||
selectedActivitySequence === 'Start & End'
|
||||
"
|
||||
:timeResultData="selectCfmSeqSE"
|
||||
:select="isSubmitStartAndEnd"
|
||||
></ResultDot>
|
||||
<ResultArrow
|
||||
v-if="
|
||||
selectedRuleType === 'Activity sequence' &&
|
||||
selectedActivitySequence === 'Sequence' &&
|
||||
selectedMode === 'Directly follows'
|
||||
"
|
||||
:data="state.selectCfmSeqDirectly"
|
||||
:select="isSubmitCfmSeqDirectly"
|
||||
></ResultArrow>
|
||||
<ResultArrow
|
||||
v-if="
|
||||
selectedRuleType === 'Activity sequence' &&
|
||||
selectedActivitySequence === 'Sequence' &&
|
||||
selectedMode === 'Eventually follows'
|
||||
"
|
||||
:data="state.selectCfmSeqEventually"
|
||||
:select="isSubmitCfmSeqEventually"
|
||||
></ResultArrow>
|
||||
<!-- Activity duration -->
|
||||
<ResultCheck v-if="selectedRuleType === 'Activity duration'" :title="'Activities include'" :data="state.durationData" :select="isSubmitDurationData"></ResultCheck>
|
||||
<ResultCheck
|
||||
v-if="selectedRuleType === 'Activity duration'"
|
||||
:title="'Activities include'"
|
||||
:data="state.durationData"
|
||||
:select="isSubmitDurationData"
|
||||
></ResultCheck>
|
||||
<!-- Processing time -->
|
||||
<ResultDot v-if="selectedRuleType === 'Processing time' && selectedProcessScope === 'End to end' && selectedActSeqMore === 'Start'" :timeResultData="state.selectCfmPtEteStart" :select="isSubmitCfmPtEteStart"></ResultDot>
|
||||
<ResultDot v-if="selectedRuleType === 'Processing time' && selectedProcessScope === 'End to end' && selectedActSeqMore === 'End'" :timeResultData="state.selectCfmPtEteEnd" :select="isSubmitCfmPtEteEnd"></ResultDot>
|
||||
<ResultDot v-if="selectedRuleType === 'Processing time' && selectedProcessScope === 'End to end' && selectedActSeqMore === 'Start & End'" :timeResultData="selectCfmPtEteSE" :select="isSubmitCfmPtEteSE"></ResultDot>
|
||||
<ResultDot v-if="selectedRuleType === 'Processing time' && selectedProcessScope === 'Partial' && selectedActSeqFromTo === 'From'" :timeResultData="state.selectCfmPtPStart" :select="isSubmitCfmPtPStart"></ResultDot>
|
||||
<ResultDot v-if="selectedRuleType === 'Processing time' && selectedProcessScope === 'Partial' && selectedActSeqFromTo === 'To'" :timeResultData="state.selectCfmPtPEnd" :select="isSubmitCfmPtPEnd"></ResultDot>
|
||||
<ResultDot v-if="selectedRuleType === 'Processing time' && selectedProcessScope === 'Partial' && selectedActSeqFromTo === 'From & To'" :timeResultData="selectCfmPtPSE" :select="isSubmitCfmPtPSE"></ResultDot>
|
||||
<ResultDot
|
||||
v-if="
|
||||
selectedRuleType === 'Processing time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'Start'
|
||||
"
|
||||
:timeResultData="state.selectCfmPtEteStart"
|
||||
:select="isSubmitCfmPtEteStart"
|
||||
></ResultDot>
|
||||
<ResultDot
|
||||
v-if="
|
||||
selectedRuleType === 'Processing time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'End'
|
||||
"
|
||||
:timeResultData="state.selectCfmPtEteEnd"
|
||||
:select="isSubmitCfmPtEteEnd"
|
||||
></ResultDot>
|
||||
<ResultDot
|
||||
v-if="
|
||||
selectedRuleType === 'Processing time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'Start & End'
|
||||
"
|
||||
:timeResultData="selectCfmPtEteSE"
|
||||
:select="isSubmitCfmPtEteSE"
|
||||
></ResultDot>
|
||||
<ResultDot
|
||||
v-if="
|
||||
selectedRuleType === 'Processing time' &&
|
||||
selectedProcessScope === 'Partial' &&
|
||||
selectedActSeqFromTo === 'From'
|
||||
"
|
||||
:timeResultData="state.selectCfmPtPStart"
|
||||
:select="isSubmitCfmPtPStart"
|
||||
></ResultDot>
|
||||
<ResultDot
|
||||
v-if="
|
||||
selectedRuleType === 'Processing time' &&
|
||||
selectedProcessScope === 'Partial' &&
|
||||
selectedActSeqFromTo === 'To'
|
||||
"
|
||||
:timeResultData="state.selectCfmPtPEnd"
|
||||
:select="isSubmitCfmPtPEnd"
|
||||
></ResultDot>
|
||||
<ResultDot
|
||||
v-if="
|
||||
selectedRuleType === 'Processing time' &&
|
||||
selectedProcessScope === 'Partial' &&
|
||||
selectedActSeqFromTo === 'From & To'
|
||||
"
|
||||
:timeResultData="selectCfmPtPSE"
|
||||
:select="isSubmitCfmPtPSE"
|
||||
></ResultDot>
|
||||
<!-- Waiting time -->
|
||||
<ResultDot v-if="selectedRuleType === 'Waiting time' && selectedProcessScope === 'End to end' && selectedActSeqMore === 'Start'" :timeResultData="state.selectCfmWtEteStart" :select="isSubmitCfmWtEteStart"></ResultDot>
|
||||
<ResultDot v-if="selectedRuleType === 'Waiting time' && selectedProcessScope === 'End to end' && selectedActSeqMore === 'End'" :timeResultData="state.selectCfmWtEteEnd" :select="isSubmitCfmWtEteEnd"></ResultDot>
|
||||
<ResultDot v-if="selectedRuleType === 'Waiting time' && selectedProcessScope === 'End to end' && selectedActSeqMore === 'Start & End'" :timeResultData="selectCfmWtEteSE" :select="isSubmitCfmWtEteSE"></ResultDot>
|
||||
<ResultDot v-if="selectedRuleType === 'Waiting time' && selectedProcessScope === 'Partial' && selectedActSeqFromTo === 'From'" :timeResultData="state.selectCfmWtPStart" :select="isSubmitCfmWtPStart"></ResultDot>
|
||||
<ResultDot v-if="selectedRuleType === 'Waiting time' && selectedProcessScope === 'Partial' && selectedActSeqFromTo === 'To'" :timeResultData="state.selectCfmWtPEnd" :select="isSubmitCfmWtPEnd"></ResultDot>
|
||||
<ResultDot v-if="selectedRuleType === 'Waiting time' && selectedProcessScope === 'Partial' && selectedActSeqFromTo === 'From & To'" :timeResultData="selectCfmWtPSE" :select="isSubmitCfmWtPSE"></ResultDot>
|
||||
<ResultDot
|
||||
v-if="
|
||||
selectedRuleType === 'Waiting time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'Start'
|
||||
"
|
||||
:timeResultData="state.selectCfmWtEteStart"
|
||||
:select="isSubmitCfmWtEteStart"
|
||||
></ResultDot>
|
||||
<ResultDot
|
||||
v-if="
|
||||
selectedRuleType === 'Waiting time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'End'
|
||||
"
|
||||
:timeResultData="state.selectCfmWtEteEnd"
|
||||
:select="isSubmitCfmWtEteEnd"
|
||||
></ResultDot>
|
||||
<ResultDot
|
||||
v-if="
|
||||
selectedRuleType === 'Waiting time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'Start & End'
|
||||
"
|
||||
:timeResultData="selectCfmWtEteSE"
|
||||
:select="isSubmitCfmWtEteSE"
|
||||
></ResultDot>
|
||||
<ResultDot
|
||||
v-if="
|
||||
selectedRuleType === 'Waiting time' &&
|
||||
selectedProcessScope === 'Partial' &&
|
||||
selectedActSeqFromTo === 'From'
|
||||
"
|
||||
:timeResultData="state.selectCfmWtPStart"
|
||||
:select="isSubmitCfmWtPStart"
|
||||
></ResultDot>
|
||||
<ResultDot
|
||||
v-if="
|
||||
selectedRuleType === 'Waiting time' &&
|
||||
selectedProcessScope === 'Partial' &&
|
||||
selectedActSeqFromTo === 'To'
|
||||
"
|
||||
:timeResultData="state.selectCfmWtPEnd"
|
||||
:select="isSubmitCfmWtPEnd"
|
||||
></ResultDot>
|
||||
<ResultDot
|
||||
v-if="
|
||||
selectedRuleType === 'Waiting time' &&
|
||||
selectedProcessScope === 'Partial' &&
|
||||
selectedActSeqFromTo === 'From & To'
|
||||
"
|
||||
:timeResultData="selectCfmWtPSE"
|
||||
:select="isSubmitCfmWtPSE"
|
||||
></ResultDot>
|
||||
<!-- Cycle time -->
|
||||
<ResultDot v-if="selectedRuleType === 'Cycle time' && selectedProcessScope === 'End to end' && selectedActSeqMore === 'Start'" :timeResultData="state.selectCfmCtEteStart" :select="isSubmitCfmCtEteStart"></ResultDot>
|
||||
<ResultDot v-if="selectedRuleType === 'Cycle time' && selectedProcessScope === 'End to end' && selectedActSeqMore === 'End'" :timeResultData="state.selectCfmCtEteEnd" :select="isSubmitCfmCtEteEnd"></ResultDot>
|
||||
<ResultDot v-if="selectedRuleType === 'Cycle time' && selectedProcessScope === 'End to end' && selectedActSeqMore === 'Start & End'" :timeResultData="selectCfmCtEteSE" :select="isSubmitCfmCtEteSE"></ResultDot>
|
||||
<ResultDot
|
||||
v-if="
|
||||
selectedRuleType === 'Cycle time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'Start'
|
||||
"
|
||||
:timeResultData="state.selectCfmCtEteStart"
|
||||
:select="isSubmitCfmCtEteStart"
|
||||
></ResultDot>
|
||||
<ResultDot
|
||||
v-if="
|
||||
selectedRuleType === 'Cycle time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'End'
|
||||
"
|
||||
:timeResultData="state.selectCfmCtEteEnd"
|
||||
:select="isSubmitCfmCtEteEnd"
|
||||
></ResultDot>
|
||||
<ResultDot
|
||||
v-if="
|
||||
selectedRuleType === 'Cycle time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'Start & End'
|
||||
"
|
||||
:timeResultData="selectCfmCtEteSE"
|
||||
:select="isSubmitCfmCtEteSE"
|
||||
></ResultDot>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
@@ -41,18 +193,49 @@
|
||||
* scrollable display of check results.
|
||||
*/
|
||||
|
||||
import { reactive, computed, onBeforeUnmount } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useConformanceStore } from '@/stores/conformance';
|
||||
import emitter from '@/utils/emitter';
|
||||
import ResultCheck from '@/components/Discover/Conformance/ConformanceSidebar/ResultCheck.vue';
|
||||
import ResultArrow from '@/components/Discover/Conformance/ConformanceSidebar/ResultArrow.vue';
|
||||
import ResultDot from '@/components/Discover/Conformance/ConformanceSidebar/ResultDot.vue';
|
||||
import { reactive, computed, onBeforeUnmount } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useConformanceStore } from "@/stores/conformance";
|
||||
import emitter from "@/utils/emitter";
|
||||
import ResultCheck from "@/components/Discover/Conformance/ConformanceSidebar/ResultCheck.vue";
|
||||
import ResultArrow from "@/components/Discover/Conformance/ConformanceSidebar/ResultArrow.vue";
|
||||
import ResultDot from "@/components/Discover/Conformance/ConformanceSidebar/ResultDot.vue";
|
||||
|
||||
const conformanceStore = useConformanceStore();
|
||||
const { selectedRuleType, selectedActivitySequence, selectedMode, selectedProcessScope, selectedActSeqMore, selectedActSeqFromTo, isStartSelected, isEndSelected } = storeToRefs(conformanceStore);
|
||||
const {
|
||||
selectedRuleType,
|
||||
selectedActivitySequence,
|
||||
selectedMode,
|
||||
selectedProcessScope,
|
||||
selectedActSeqMore,
|
||||
selectedActSeqFromTo,
|
||||
isStartSelected,
|
||||
isEndSelected,
|
||||
} = storeToRefs(conformanceStore);
|
||||
|
||||
const props = defineProps(['isSubmit', 'isSubmitTask', 'isSubmitStartAndEnd', 'isSubmitCfmSeqDirectly', 'isSubmitCfmSeqEventually', 'isSubmitDurationData', 'isSubmitCfmPtEteStart', 'isSubmitCfmPtEteEnd', 'isSubmitCfmPtEteSE', 'isSubmitCfmPtPStart', 'isSubmitCfmPtPEnd', 'isSubmitCfmPtPSE', 'isSubmitCfmWtEteStart', 'isSubmitCfmWtEteEnd', 'isSubmitCfmWtEteSE', 'isSubmitCfmWtPStart', 'isSubmitCfmWtPEnd', 'isSubmitCfmWtPSE', 'isSubmitCfmCtEteStart', 'isSubmitCfmCtEteEnd', 'isSubmitCfmCtEteSE']);
|
||||
const props = defineProps([
|
||||
"isSubmit",
|
||||
"isSubmitTask",
|
||||
"isSubmitStartAndEnd",
|
||||
"isSubmitCfmSeqDirectly",
|
||||
"isSubmitCfmSeqEventually",
|
||||
"isSubmitDurationData",
|
||||
"isSubmitCfmPtEteStart",
|
||||
"isSubmitCfmPtEteEnd",
|
||||
"isSubmitCfmPtEteSE",
|
||||
"isSubmitCfmPtPStart",
|
||||
"isSubmitCfmPtPEnd",
|
||||
"isSubmitCfmPtPSE",
|
||||
"isSubmitCfmWtEteStart",
|
||||
"isSubmitCfmWtEteEnd",
|
||||
"isSubmitCfmWtEteSE",
|
||||
"isSubmitCfmWtPStart",
|
||||
"isSubmitCfmWtPEnd",
|
||||
"isSubmitCfmWtPSE",
|
||||
"isSubmitCfmCtEteStart",
|
||||
"isSubmitCfmCtEteEnd",
|
||||
"isSubmitCfmCtEteSE",
|
||||
]);
|
||||
|
||||
const state = reactive({
|
||||
containstTasksData: null,
|
||||
@@ -87,10 +270,10 @@ const state = reactive({
|
||||
|
||||
const selectCfmSeqSE = computed(() => {
|
||||
const data = [];
|
||||
if(state.selectCfmSeqStart) data.push(state.selectCfmSeqStart);
|
||||
if(state.selectCfmSeqEnd) data.push(state.selectCfmSeqEnd);
|
||||
if (state.selectCfmSeqStart) data.push(state.selectCfmSeqStart);
|
||||
if (state.selectCfmSeqEnd) data.push(state.selectCfmSeqEnd);
|
||||
data.sort((a, b) => {
|
||||
const order = { 'Start': 1, 'End': 2};
|
||||
const order = { Start: 1, End: 2 };
|
||||
return order[a.category] - order[b.category];
|
||||
});
|
||||
return data;
|
||||
@@ -98,10 +281,10 @@ const selectCfmSeqSE = computed(() => {
|
||||
|
||||
const selectCfmPtEteSE = computed(() => {
|
||||
const data = [];
|
||||
if(state.selectCfmPtEteSEStart) data.push(state.selectCfmPtEteSEStart);
|
||||
if(state.selectCfmPtEteSEEnd) data.push(state.selectCfmPtEteSEEnd);
|
||||
if (state.selectCfmPtEteSEStart) data.push(state.selectCfmPtEteSEStart);
|
||||
if (state.selectCfmPtEteSEEnd) data.push(state.selectCfmPtEteSEEnd);
|
||||
data.sort((a, b) => {
|
||||
const order = { 'Start': 1, 'End': 2};
|
||||
const order = { Start: 1, End: 2 };
|
||||
return order[a.category] - order[b.category];
|
||||
});
|
||||
return data;
|
||||
@@ -109,10 +292,10 @@ const selectCfmPtEteSE = computed(() => {
|
||||
|
||||
const selectCfmPtPSE = computed(() => {
|
||||
const data = [];
|
||||
if(state.selectCfmPtPSEStart) data.push(state.selectCfmPtPSEStart);
|
||||
if(state.selectCfmPtPSEEnd) data.push(state.selectCfmPtPSEEnd);
|
||||
if (state.selectCfmPtPSEStart) data.push(state.selectCfmPtPSEStart);
|
||||
if (state.selectCfmPtPSEEnd) data.push(state.selectCfmPtPSEEnd);
|
||||
data.sort((a, b) => {
|
||||
const order = { 'From': 1, 'To': 2};
|
||||
const order = { From: 1, To: 2 };
|
||||
return order[a.category] - order[b.category];
|
||||
});
|
||||
return data;
|
||||
@@ -120,10 +303,10 @@ const selectCfmPtPSE = computed(() => {
|
||||
|
||||
const selectCfmWtEteSE = computed(() => {
|
||||
const data = [];
|
||||
if(state.selectCfmWtEteSEStart) data.push(state.selectCfmWtEteSEStart);
|
||||
if(state.selectCfmWtEteSEEnd) data.push(state.selectCfmWtEteSEEnd);
|
||||
if (state.selectCfmWtEteSEStart) data.push(state.selectCfmWtEteSEStart);
|
||||
if (state.selectCfmWtEteSEEnd) data.push(state.selectCfmWtEteSEEnd);
|
||||
data.sort((a, b) => {
|
||||
const order = { 'Start': 1, 'End': 2};
|
||||
const order = { Start: 1, End: 2 };
|
||||
return order[a.category] - order[b.category];
|
||||
});
|
||||
return data;
|
||||
@@ -131,10 +314,10 @@ const selectCfmWtEteSE = computed(() => {
|
||||
|
||||
const selectCfmWtPSE = computed(() => {
|
||||
const data = [];
|
||||
if(state.selectCfmWtPSEStart) data.push(state.selectCfmWtPSEStart);
|
||||
if(state.selectCfmWtPSEEnd) data.push(state.selectCfmWtPSEEnd);
|
||||
if (state.selectCfmWtPSEStart) data.push(state.selectCfmWtPSEStart);
|
||||
if (state.selectCfmWtPSEEnd) data.push(state.selectCfmWtPSEEnd);
|
||||
data.sort((a, b) => {
|
||||
const order = { 'From': 1, 'To': 2};
|
||||
const order = { From: 1, To: 2 };
|
||||
return order[a.category] - order[b.category];
|
||||
});
|
||||
return data;
|
||||
@@ -142,10 +325,10 @@ const selectCfmWtPSE = computed(() => {
|
||||
|
||||
const selectCfmCtEteSE = computed(() => {
|
||||
const data = [];
|
||||
if(state.selectCfmCtEteSEStart) data.push(state.selectCfmCtEteSEStart);
|
||||
if(state.selectCfmCtEteSEEnd) data.push(state.selectCfmCtEteSEEnd);
|
||||
if (state.selectCfmCtEteSEStart) data.push(state.selectCfmCtEteSEStart);
|
||||
if (state.selectCfmCtEteSEEnd) data.push(state.selectCfmCtEteSEEnd);
|
||||
data.sort((a, b) => {
|
||||
const order = { 'Start': 1, 'End': 2};
|
||||
const order = { Start: 1, End: 2 };
|
||||
return order[a.category] - order[b.category];
|
||||
});
|
||||
return data;
|
||||
@@ -186,35 +369,35 @@ function reset() {
|
||||
}
|
||||
|
||||
// created() logic
|
||||
emitter.on('actListData', (data) => {
|
||||
emitter.on("actListData", (data) => {
|
||||
state.containstTasksData = data;
|
||||
});
|
||||
emitter.on('actRadioData', (newData) => {
|
||||
emitter.on("actRadioData", (newData) => {
|
||||
const data = JSON.parse(JSON.stringify(newData)); // Deep copy the original cases data
|
||||
|
||||
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']
|
||||
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) => {
|
||||
@@ -225,64 +408,65 @@ emitter.on('actRadioData', (newData) => {
|
||||
state[mainSelector] = data;
|
||||
};
|
||||
|
||||
if (categoryMapping[data.category]) {
|
||||
const [category, mainSelector, secondarySelector] = categoryMapping[data.category];
|
||||
if (secondarySelector) {
|
||||
updateSelection(data.category, mainSelector, secondarySelector);
|
||||
} else {
|
||||
data.category = category;
|
||||
state[mainSelector] = [data];
|
||||
}
|
||||
} else if (selectedRuleType.value === 'Activity duration') {
|
||||
state.durationData = [data.task];
|
||||
if (categoryMapping[data.category]) {
|
||||
const [category, mainSelector, secondarySelector] =
|
||||
categoryMapping[data.category];
|
||||
if (secondarySelector) {
|
||||
updateSelection(data.category, mainSelector, secondarySelector);
|
||||
} else {
|
||||
data.category = category;
|
||||
state[mainSelector] = [data];
|
||||
}
|
||||
} else if (selectedRuleType.value === "Activity duration") {
|
||||
state.durationData = [data.task];
|
||||
}
|
||||
});
|
||||
emitter.on('getListSequence', (data) => {
|
||||
emitter.on("getListSequence", (data) => {
|
||||
switch (data.category) {
|
||||
case 'cfmSeqDirectly':
|
||||
case "cfmSeqDirectly":
|
||||
state.selectCfmSeqDirectly = data.task;
|
||||
break;
|
||||
case 'cfmSeqEventually':
|
||||
case "cfmSeqEventually":
|
||||
state.selectCfmSeqEventually = data.task;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
emitter.on('reset', (data) => {
|
||||
emitter.on("reset", (data) => {
|
||||
reset();
|
||||
});
|
||||
// Clear data when switching radio buttons
|
||||
emitter.on('isRadioChange', (data) => {
|
||||
if(data) reset();
|
||||
emitter.on("isRadioChange", (data) => {
|
||||
if (data) reset();
|
||||
});
|
||||
emitter.on('isRadioProcessScopeChange', (data) => {
|
||||
if(data) reset();
|
||||
emitter.on("isRadioProcessScopeChange", (data) => {
|
||||
if (data) reset();
|
||||
});
|
||||
emitter.on('isRadioActSeqMoreChange', (data) => {
|
||||
if(data) reset();
|
||||
emitter.on("isRadioActSeqMoreChange", (data) => {
|
||||
if (data) reset();
|
||||
});
|
||||
emitter.on('isRadioActSeqFromToChange', (data) => {
|
||||
if(data) reset();
|
||||
emitter.on("isRadioActSeqFromToChange", (data) => {
|
||||
if (data) reset();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
emitter.off('actListData');
|
||||
emitter.off('actRadioData');
|
||||
emitter.off('getListSequence');
|
||||
emitter.off('reset');
|
||||
emitter.off('isRadioChange');
|
||||
emitter.off('isRadioProcessScopeChange');
|
||||
emitter.off('isRadioActSeqMoreChange');
|
||||
emitter.off('isRadioActSeqFromToChange');
|
||||
emitter.off("actListData");
|
||||
emitter.off("actRadioData");
|
||||
emitter.off("getListSequence");
|
||||
emitter.off("reset");
|
||||
emitter.off("isRadioChange");
|
||||
emitter.off("isRadioProcessScopeChange");
|
||||
emitter.off("isRadioActSeqMoreChange");
|
||||
emitter.off("isRadioActSeqFromToChange");
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
:deep(.disc) {
|
||||
font-variation-settings:
|
||||
'FILL' 1,
|
||||
'wght' 100,
|
||||
'GRAD' 0,
|
||||
'opsz' 20
|
||||
"FILL" 1,
|
||||
"wght" 100,
|
||||
"GRAD" 0,
|
||||
"opsz" 20;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,99 +1,345 @@
|
||||
<template>
|
||||
<section class="animate-fadein w-full h-full" >
|
||||
|
||||
<section class="animate-fadein w-full h-full">
|
||||
<!-- Have activity -->
|
||||
<ActList v-if="selectedRuleType === 'Have activity'" :data="conformanceTask" :select="isSubmitTask"></ActList>
|
||||
<ActList
|
||||
v-if="selectedRuleType === 'Have activity'"
|
||||
:data="conformanceTask"
|
||||
:select="isSubmitTask"
|
||||
></ActList>
|
||||
|
||||
<!-- Activity sequence -->
|
||||
<div v-if="selectedRuleType === 'Activity sequence' && selectedActivitySequence === 'Start & End'"
|
||||
class="flex justify-between items-center w-full h-full">
|
||||
<ActRadio :title="'Start activity'" :select="isSubmitStartAndEnd?.[0].task" :data="cfmSeqStartData"
|
||||
:category="'cfmSeqStart'" :task="taskStart" :isSubmit="isSubmit" @selected-task="selectStart" class="w-1/2" />
|
||||
<ActRadio :title="'End activity'" :select="isSubmitStartAndEnd?.[1].task" :data="cfmSeqEndData"
|
||||
:category="'cfmSeqEnd'" :task="taskEnd" :isSubmit="isSubmit" @selected-task="selectEnd" class="w-1/2" />
|
||||
<div
|
||||
v-if="
|
||||
selectedRuleType === 'Activity sequence' &&
|
||||
selectedActivitySequence === 'Start & End'
|
||||
"
|
||||
class="flex justify-between items-center w-full h-full"
|
||||
>
|
||||
<ActRadio
|
||||
:title="'Start activity'"
|
||||
:select="isSubmitStartAndEnd?.[0].task"
|
||||
:data="cfmSeqStartData"
|
||||
:category="'cfmSeqStart'"
|
||||
:task="taskStart"
|
||||
:isSubmit="isSubmit"
|
||||
@selected-task="selectStart"
|
||||
class="w-1/2"
|
||||
/>
|
||||
<ActRadio
|
||||
:title="'End activity'"
|
||||
:select="isSubmitStartAndEnd?.[1].task"
|
||||
:data="cfmSeqEndData"
|
||||
:category="'cfmSeqEnd'"
|
||||
:task="taskEnd"
|
||||
:isSubmit="isSubmit"
|
||||
@selected-task="selectEnd"
|
||||
class="w-1/2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- actSeqDrag -->
|
||||
<ActSeqDrag v-if="selectedRuleType === 'Activity sequence' && selectedActivitySequence === 'Sequence'
|
||||
&& selectedMode === 'Directly follows'" :data="conformanceTask" :listSeq="isSubmitCfmSeqDirectly"
|
||||
:isSubmit="isSubmit" :category="'cfmSeqDirectly'"></ActSeqDrag>
|
||||
<ActSeqDrag v-if="selectedRuleType === 'Activity sequence' && selectedActivitySequence === 'Sequence'
|
||||
&& selectedMode === 'Eventually follows'" :data="conformanceTask" :listSeq="isSubmitCfmSeqEventually"
|
||||
:isSubmit="isSubmit" :category="'cfmSeqEventually'"></ActSeqDrag>
|
||||
<ActSeqDrag
|
||||
v-if="
|
||||
selectedRuleType === 'Activity sequence' &&
|
||||
selectedActivitySequence === 'Sequence' &&
|
||||
selectedMode === 'Directly follows'
|
||||
"
|
||||
:data="conformanceTask"
|
||||
:listSeq="isSubmitCfmSeqDirectly"
|
||||
:isSubmit="isSubmit"
|
||||
:category="'cfmSeqDirectly'"
|
||||
></ActSeqDrag>
|
||||
<ActSeqDrag
|
||||
v-if="
|
||||
selectedRuleType === 'Activity sequence' &&
|
||||
selectedActivitySequence === 'Sequence' &&
|
||||
selectedMode === 'Eventually follows'
|
||||
"
|
||||
:data="conformanceTask"
|
||||
:listSeq="isSubmitCfmSeqEventually"
|
||||
:isSubmit="isSubmit"
|
||||
:category="'cfmSeqEventually'"
|
||||
></ActSeqDrag>
|
||||
|
||||
<!-- Activity duration -->
|
||||
<ActRadio v-if="selectedRuleType === 'Activity duration'" :title="'Activities include'"
|
||||
:select="isSubmitDurationData?.[0]" :data="conformanceTask" :category="'cfmDur'" :isSubmit="isSubmit"/>
|
||||
<ActRadio
|
||||
v-if="selectedRuleType === 'Activity duration'"
|
||||
:title="'Activities include'"
|
||||
:select="isSubmitDurationData?.[0]"
|
||||
:data="conformanceTask"
|
||||
:category="'cfmDur'"
|
||||
:isSubmit="isSubmit"
|
||||
/>
|
||||
|
||||
<!-- Processing time -->
|
||||
<ActRadio v-if="selectedRuleType === 'Processing time' && selectedProcessScope === 'End to end'
|
||||
&& selectedActSeqMore === 'Start'" :title="'Start'" :select="isSubmitCfmPtEteStart?.[0].task"
|
||||
:data="cfmPtEteStartData" :category="'cfmPtEteStart'" :isSubmit="isSubmit" />
|
||||
<ActRadio v-if="selectedRuleType === 'Processing time' && selectedProcessScope === 'End to end'
|
||||
&& selectedActSeqMore === 'End'" :title="'End'" :select="isSubmitCfmPtEteEnd?.[0].task" :data="cfmPtEteEndData"
|
||||
:category="'cfmPtEteEnd'" :isSubmit="isSubmit" />
|
||||
<div v-if="selectedRuleType === 'Processing time' && selectedProcessScope === 'End to end'
|
||||
&& selectedActSeqMore === 'Start & End'" class="flex justify-between items-center w-full h-full">
|
||||
<ActRadio :title="'Start'" :select="isSubmitCfmPtEteSE?.[0].task" :data="cfmPtEteSEStartData"
|
||||
:category="'cfmPtEteSEStart'" :task="taskStart" :isSubmit="isSubmit" @selected-task="selectStart" class="w-1/2" />
|
||||
<ActRadio :title="'End'" :select="isSubmitCfmPtEteSE?.[1].task" :data="cfmPtEteSEEndData"
|
||||
:category="'cfmPtEteSEEnd'" :task="taskEnd" :isSubmit="isSubmit" @selected-task="selectEnd" class="w-1/2" />
|
||||
<!-- Processing time -->
|
||||
<ActRadio
|
||||
v-if="
|
||||
selectedRuleType === 'Processing time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'Start'
|
||||
"
|
||||
:title="'Start'"
|
||||
:select="isSubmitCfmPtEteStart?.[0].task"
|
||||
:data="cfmPtEteStartData"
|
||||
:category="'cfmPtEteStart'"
|
||||
:isSubmit="isSubmit"
|
||||
/>
|
||||
<ActRadio
|
||||
v-if="
|
||||
selectedRuleType === 'Processing time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'End'
|
||||
"
|
||||
:title="'End'"
|
||||
:select="isSubmitCfmPtEteEnd?.[0].task"
|
||||
:data="cfmPtEteEndData"
|
||||
:category="'cfmPtEteEnd'"
|
||||
:isSubmit="isSubmit"
|
||||
/>
|
||||
<div
|
||||
v-if="
|
||||
selectedRuleType === 'Processing time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'Start & End'
|
||||
"
|
||||
class="flex justify-between items-center w-full h-full"
|
||||
>
|
||||
<ActRadio
|
||||
:title="'Start'"
|
||||
:select="isSubmitCfmPtEteSE?.[0].task"
|
||||
:data="cfmPtEteSEStartData"
|
||||
:category="'cfmPtEteSEStart'"
|
||||
:task="taskStart"
|
||||
:isSubmit="isSubmit"
|
||||
@selected-task="selectStart"
|
||||
class="w-1/2"
|
||||
/>
|
||||
<ActRadio
|
||||
:title="'End'"
|
||||
:select="isSubmitCfmPtEteSE?.[1].task"
|
||||
:data="cfmPtEteSEEndData"
|
||||
:category="'cfmPtEteSEEnd'"
|
||||
:task="taskEnd"
|
||||
:isSubmit="isSubmit"
|
||||
@selected-task="selectEnd"
|
||||
class="w-1/2"
|
||||
/>
|
||||
</div>
|
||||
<ActRadio v-if="selectedRuleType === 'Processing time' && selectedProcessScope === 'Partial'
|
||||
&& selectedActSeqFromTo === 'From'" :title="'From'" :select="isSubmitCfmPtPStart?.[0].task" :data="cfmPtPStartData"
|
||||
:category="'cfmPtPStart'" :isSubmit="isSubmit" />
|
||||
<ActRadio v-if="selectedRuleType === 'Processing time' && selectedProcessScope === 'Partial'
|
||||
&& selectedActSeqFromTo === 'To'" :title="'To'" :select="isSubmitCfmPtPEnd?.[0].task" :data="cfmPtPEndData"
|
||||
:category="'cfmPtPEnd'" :isSubmit="isSubmit" />
|
||||
<div v-if="selectedRuleType === 'Processing time' && selectedProcessScope === 'Partial'
|
||||
&& selectedActSeqFromTo === 'From & To'" class="flex justify-between items-center w-full h-full">
|
||||
<ActRadio :title="'From'" :select="isSubmitCfmPtPSE?.[0].task" :data="cfmPtPSEStartData"
|
||||
class="w-1/2" :category="'cfmPtPSEStart'" :task="taskStart" :isSubmit="isSubmit" @selected-task="selectStart" />
|
||||
<ActRadio :title="'To'" :select="isSubmitCfmPtPSE?.[1].task" :data="cfmPtPSEEndData" class="w-1/2"
|
||||
:category="'cfmPtPSEEnd'" :task="taskEnd" :isSubmit="isSubmit" @selected-task="selectEnd" />
|
||||
<ActRadio
|
||||
v-if="
|
||||
selectedRuleType === 'Processing time' &&
|
||||
selectedProcessScope === 'Partial' &&
|
||||
selectedActSeqFromTo === 'From'
|
||||
"
|
||||
:title="'From'"
|
||||
:select="isSubmitCfmPtPStart?.[0].task"
|
||||
:data="cfmPtPStartData"
|
||||
:category="'cfmPtPStart'"
|
||||
:isSubmit="isSubmit"
|
||||
/>
|
||||
<ActRadio
|
||||
v-if="
|
||||
selectedRuleType === 'Processing time' &&
|
||||
selectedProcessScope === 'Partial' &&
|
||||
selectedActSeqFromTo === 'To'
|
||||
"
|
||||
:title="'To'"
|
||||
:select="isSubmitCfmPtPEnd?.[0].task"
|
||||
:data="cfmPtPEndData"
|
||||
:category="'cfmPtPEnd'"
|
||||
:isSubmit="isSubmit"
|
||||
/>
|
||||
<div
|
||||
v-if="
|
||||
selectedRuleType === 'Processing time' &&
|
||||
selectedProcessScope === 'Partial' &&
|
||||
selectedActSeqFromTo === 'From & To'
|
||||
"
|
||||
class="flex justify-between items-center w-full h-full"
|
||||
>
|
||||
<ActRadio
|
||||
:title="'From'"
|
||||
:select="isSubmitCfmPtPSE?.[0].task"
|
||||
:data="cfmPtPSEStartData"
|
||||
class="w-1/2"
|
||||
:category="'cfmPtPSEStart'"
|
||||
:task="taskStart"
|
||||
:isSubmit="isSubmit"
|
||||
@selected-task="selectStart"
|
||||
/>
|
||||
<ActRadio
|
||||
:title="'To'"
|
||||
:select="isSubmitCfmPtPSE?.[1].task"
|
||||
:data="cfmPtPSEEndData"
|
||||
class="w-1/2"
|
||||
:category="'cfmPtPSEEnd'"
|
||||
:task="taskEnd"
|
||||
:isSubmit="isSubmit"
|
||||
@selected-task="selectEnd"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Waiting time -->
|
||||
<ActRadio v-if="selectedRuleType === 'Waiting time' && selectedProcessScope === 'End to end'
|
||||
&& selectedActSeqMore === 'Start'" :title="'Start'" :select="isSubmitCfmWtEteStart?.[0].task"
|
||||
:data="cfmWtEteStartData" :category="'cfmWtEteStart'" :isSubmit="isSubmit" />
|
||||
<ActRadio v-if="selectedRuleType === 'Waiting time' && selectedProcessScope === 'End to end'
|
||||
&& selectedActSeqMore === 'End'" :title="'End'" :select="isSubmitCfmWtEteEnd?.[0].task"
|
||||
:data="cfmWtEteEndData" :category="'cfmWtEteEnd'" :isSubmit="isSubmit" />
|
||||
<div v-if="selectedRuleType === 'Waiting time' && selectedProcessScope === 'End to end'
|
||||
&& selectedActSeqMore === 'Start & End'" class="flex justify-between items-center w-full h-full">
|
||||
<ActRadio :title="'Start'" :select="isSubmitCfmWtEteSE?.[0].task" :data="cfmWtEteSEStartData" class="w-1/2"
|
||||
:category="'cfmWtEteSEStart'" :task="taskStart" :isSubmit="isSubmit" @selected-task="selectStart" />
|
||||
<ActRadio :title="'End'" :select="isSubmitCfmWtEteSE?.[1].task" :data="cfmWtEteSEEndData" class="w-1/2"
|
||||
:category="'cfmWtEteSEEnd'" :task="taskEnd" :isSubmit="isSubmit" @selected-task="selectEnd" />
|
||||
<ActRadio
|
||||
v-if="
|
||||
selectedRuleType === 'Waiting time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'Start'
|
||||
"
|
||||
:title="'Start'"
|
||||
:select="isSubmitCfmWtEteStart?.[0].task"
|
||||
:data="cfmWtEteStartData"
|
||||
:category="'cfmWtEteStart'"
|
||||
:isSubmit="isSubmit"
|
||||
/>
|
||||
<ActRadio
|
||||
v-if="
|
||||
selectedRuleType === 'Waiting time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'End'
|
||||
"
|
||||
:title="'End'"
|
||||
:select="isSubmitCfmWtEteEnd?.[0].task"
|
||||
:data="cfmWtEteEndData"
|
||||
:category="'cfmWtEteEnd'"
|
||||
:isSubmit="isSubmit"
|
||||
/>
|
||||
<div
|
||||
v-if="
|
||||
selectedRuleType === 'Waiting time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'Start & End'
|
||||
"
|
||||
class="flex justify-between items-center w-full h-full"
|
||||
>
|
||||
<ActRadio
|
||||
:title="'Start'"
|
||||
:select="isSubmitCfmWtEteSE?.[0].task"
|
||||
:data="cfmWtEteSEStartData"
|
||||
class="w-1/2"
|
||||
:category="'cfmWtEteSEStart'"
|
||||
:task="taskStart"
|
||||
:isSubmit="isSubmit"
|
||||
@selected-task="selectStart"
|
||||
/>
|
||||
<ActRadio
|
||||
:title="'End'"
|
||||
:select="isSubmitCfmWtEteSE?.[1].task"
|
||||
:data="cfmWtEteSEEndData"
|
||||
class="w-1/2"
|
||||
:category="'cfmWtEteSEEnd'"
|
||||
:task="taskEnd"
|
||||
:isSubmit="isSubmit"
|
||||
@selected-task="selectEnd"
|
||||
/>
|
||||
</div>
|
||||
<ActRadio v-if="selectedRuleType === 'Waiting time' && selectedProcessScope === 'Partial'
|
||||
&& selectedActSeqFromTo === 'From'" :title="'From'" :select="isSubmitCfmWtPStart?.[0].task" :data="cfmWtPStartData"
|
||||
:category="'cfmWtPStart'" :isSubmit="isSubmit" />
|
||||
<ActRadio v-if="selectedRuleType === 'Waiting time' && selectedProcessScope === 'Partial'
|
||||
&& selectedActSeqFromTo === 'To'" :title="'To'" :select="isSubmitCfmWtPEnd?.[0].task"
|
||||
:data="cfmWtPEndData" :category="'cfmWtPEnd'" :isSubmit="isSubmit" />
|
||||
<div v-if="selectedRuleType === 'Waiting time' && selectedProcessScope === 'Partial'
|
||||
&& selectedActSeqFromTo === 'From & To'" class="flex justify-between items-center w-full h-full">
|
||||
<ActRadio :title="'From'" :select="isSubmitCfmWtPSE?.[0].task" :data="cfmWtPSEStartData"
|
||||
class="w-1/2" :category="'cfmWtPSEStart'" :task="taskStart" :isSubmit="isSubmit" @selected-task="selectStart" />
|
||||
<ActRadio :title="'To'" :select="isSubmitCfmWtPSE?.[1].task" :data="cfmWtPSEEndData"
|
||||
class="w-1/2" :category="'cfmWtPSEEnd'" :task="taskEnd" :isSubmit="isSubmit" @selected-task="selectEnd" />
|
||||
<ActRadio
|
||||
v-if="
|
||||
selectedRuleType === 'Waiting time' &&
|
||||
selectedProcessScope === 'Partial' &&
|
||||
selectedActSeqFromTo === 'From'
|
||||
"
|
||||
:title="'From'"
|
||||
:select="isSubmitCfmWtPStart?.[0].task"
|
||||
:data="cfmWtPStartData"
|
||||
:category="'cfmWtPStart'"
|
||||
:isSubmit="isSubmit"
|
||||
/>
|
||||
<ActRadio
|
||||
v-if="
|
||||
selectedRuleType === 'Waiting time' &&
|
||||
selectedProcessScope === 'Partial' &&
|
||||
selectedActSeqFromTo === 'To'
|
||||
"
|
||||
:title="'To'"
|
||||
:select="isSubmitCfmWtPEnd?.[0].task"
|
||||
:data="cfmWtPEndData"
|
||||
:category="'cfmWtPEnd'"
|
||||
:isSubmit="isSubmit"
|
||||
/>
|
||||
<div
|
||||
v-if="
|
||||
selectedRuleType === 'Waiting time' &&
|
||||
selectedProcessScope === 'Partial' &&
|
||||
selectedActSeqFromTo === 'From & To'
|
||||
"
|
||||
class="flex justify-between items-center w-full h-full"
|
||||
>
|
||||
<ActRadio
|
||||
:title="'From'"
|
||||
:select="isSubmitCfmWtPSE?.[0].task"
|
||||
:data="cfmWtPSEStartData"
|
||||
class="w-1/2"
|
||||
:category="'cfmWtPSEStart'"
|
||||
:task="taskStart"
|
||||
:isSubmit="isSubmit"
|
||||
@selected-task="selectStart"
|
||||
/>
|
||||
<ActRadio
|
||||
:title="'To'"
|
||||
:select="isSubmitCfmWtPSE?.[1].task"
|
||||
:data="cfmWtPSEEndData"
|
||||
class="w-1/2"
|
||||
:category="'cfmWtPSEEnd'"
|
||||
:task="taskEnd"
|
||||
:isSubmit="isSubmit"
|
||||
@selected-task="selectEnd"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Cycle time -->
|
||||
<ActRadio v-if="selectedRuleType === 'Cycle time' && selectedProcessScope === 'End to end'
|
||||
&& selectedActSeqMore === 'Start'" :title="'Start'" :select="isSubmitCfmCtEteStart?.[0].task"
|
||||
:data="cfmCtEteStartData" :category="'cfmCtEteStart'" :isSubmit="isSubmit" />
|
||||
<ActRadio v-if="selectedRuleType === 'Cycle time' && selectedProcessScope === 'End to end'
|
||||
&& selectedActSeqMore === 'End'" :title="'End'" :select="isSubmitCfmCtEteEnd?.[0].task" :data="cfmCtEteEndData"
|
||||
:category="'cfmCtEteEnd'" :isSubmit="isSubmit" />
|
||||
<div v-if="selectedRuleType === 'Cycle time' && selectedProcessScope === 'End to end' && selectedActSeqMore === 'Start & End'"
|
||||
class="flex justify-between items-center w-full h-full">
|
||||
<ActRadio :title="'Start'" :select="isSubmitCfmCtEteSE?.[0].task" :data="cfmCtEteSEStartData" class="w-1/2"
|
||||
:category="'cfmCtEteSEStart'" :task="taskStart" :isSubmit="isSubmit" @selected-task="selectStart" />
|
||||
<ActRadio :title="'End'" :select="isSubmitCfmCtEteSE?.[1].task" :data="cfmCtEteSEEndData" class="w-1/2"
|
||||
:category="'cfmCtEteSEEnd'" :task="taskEnd" :isSubmit="isSubmit" @selected-task="selectEnd" />
|
||||
<ActRadio
|
||||
v-if="
|
||||
selectedRuleType === 'Cycle time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'Start'
|
||||
"
|
||||
:title="'Start'"
|
||||
:select="isSubmitCfmCtEteStart?.[0].task"
|
||||
:data="cfmCtEteStartData"
|
||||
:category="'cfmCtEteStart'"
|
||||
:isSubmit="isSubmit"
|
||||
/>
|
||||
<ActRadio
|
||||
v-if="
|
||||
selectedRuleType === 'Cycle time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'End'
|
||||
"
|
||||
:title="'End'"
|
||||
:select="isSubmitCfmCtEteEnd?.[0].task"
|
||||
:data="cfmCtEteEndData"
|
||||
:category="'cfmCtEteEnd'"
|
||||
:isSubmit="isSubmit"
|
||||
/>
|
||||
<div
|
||||
v-if="
|
||||
selectedRuleType === 'Cycle time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'Start & End'
|
||||
"
|
||||
class="flex justify-between items-center w-full h-full"
|
||||
>
|
||||
<ActRadio
|
||||
:title="'Start'"
|
||||
:select="isSubmitCfmCtEteSE?.[0].task"
|
||||
:data="cfmCtEteSEStartData"
|
||||
class="w-1/2"
|
||||
:category="'cfmCtEteSEStart'"
|
||||
:task="taskStart"
|
||||
:isSubmit="isSubmit"
|
||||
@selected-task="selectStart"
|
||||
/>
|
||||
<ActRadio
|
||||
:title="'End'"
|
||||
:select="isSubmitCfmCtEteSE?.[1].task"
|
||||
:data="cfmCtEteSEEndData"
|
||||
class="w-1/2"
|
||||
:category="'cfmCtEteSEEnd'"
|
||||
:task="taskEnd"
|
||||
:isSubmit="isSubmit"
|
||||
@selected-task="selectEnd"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
@@ -110,31 +356,75 @@
|
||||
* check result statistics.
|
||||
*/
|
||||
|
||||
import { ref, computed, watch } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useLoadingStore } from '@/stores/loading';
|
||||
import { useConformanceStore } from '@/stores/conformance';
|
||||
import emitter from '@/utils/emitter';
|
||||
import ActList from './ActList.vue';
|
||||
import ActRadio from './ActRadio.vue';
|
||||
import ActSeqDrag from './ActSeqDrag.vue';
|
||||
import { ref, computed, watch } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useLoadingStore } from "@/stores/loading";
|
||||
import { useConformanceStore } from "@/stores/conformance";
|
||||
import emitter from "@/utils/emitter";
|
||||
import ActList from "./ActList.vue";
|
||||
import ActRadio from "./ActRadio.vue";
|
||||
import ActSeqDrag from "./ActSeqDrag.vue";
|
||||
|
||||
const loadingStore = useLoadingStore();
|
||||
const conformanceStore = useConformanceStore();
|
||||
const { isLoading } = storeToRefs(loadingStore);
|
||||
const { selectedRuleType, selectedActivitySequence, selectedMode, selectedProcessScope, selectedActSeqMore,
|
||||
selectedActSeqFromTo, conformanceTask, cfmSeqStart, cfmSeqEnd, cfmPtEteStart, cfmPtEteEnd, cfmPtEteSE,
|
||||
cfmPtPStart, cfmPtPEnd, cfmPtPSE, cfmWtEteStart, cfmWtEteEnd, cfmWtEteSE, cfmWtPStart, cfmWtPEnd,
|
||||
cfmWtPSE, cfmCtEteStart, cfmCtEteEnd, cfmCtEteSE, isStartSelected, isEndSelected
|
||||
const {
|
||||
selectedRuleType,
|
||||
selectedActivitySequence,
|
||||
selectedMode,
|
||||
selectedProcessScope,
|
||||
selectedActSeqMore,
|
||||
selectedActSeqFromTo,
|
||||
conformanceTask,
|
||||
cfmSeqStart,
|
||||
cfmSeqEnd,
|
||||
cfmPtEteStart,
|
||||
cfmPtEteEnd,
|
||||
cfmPtEteSE,
|
||||
cfmPtPStart,
|
||||
cfmPtPEnd,
|
||||
cfmPtPSE,
|
||||
cfmWtEteStart,
|
||||
cfmWtEteEnd,
|
||||
cfmWtEteSE,
|
||||
cfmWtPStart,
|
||||
cfmWtPEnd,
|
||||
cfmWtPSE,
|
||||
cfmCtEteStart,
|
||||
cfmCtEteEnd,
|
||||
cfmCtEteSE,
|
||||
isStartSelected,
|
||||
isEndSelected,
|
||||
} = storeToRefs(conformanceStore);
|
||||
|
||||
const props = defineProps(['isSubmit', 'isSubmitTask', 'isSubmitStartAndEnd', 'isSubmitCfmSeqDirectly', 'isSubmitCfmSeqEventually',
|
||||
'isSubmitDurationData', 'isSubmitCfmPtEteStart', 'isSubmitCfmPtEteEnd', 'isSubmitCfmPtEteSE',
|
||||
'isSubmitCfmPtPStart', 'isSubmitCfmPtPEnd', 'isSubmitCfmPtPSE', 'isSubmitCfmWtEteStart',
|
||||
'isSubmitCfmWtEteEnd', 'isSubmitCfmWtEteSE', 'isSubmitCfmWtPStart', 'isSubmitCfmWtPEnd',
|
||||
'isSubmitCfmWtPSE', 'isSubmitCfmCtEteStart', 'isSubmitCfmCtEteEnd', 'isSubmitCfmCtEteSE',
|
||||
'isSubmitShowDataSeq', 'isSubmitShowDataPtEte', 'isSubmitShowDataPtP', 'isSubmitShowDataWtEte',
|
||||
'isSubmitShowDataWtP', 'isSubmitShowDataCt'
|
||||
const props = defineProps([
|
||||
"isSubmit",
|
||||
"isSubmitTask",
|
||||
"isSubmitStartAndEnd",
|
||||
"isSubmitCfmSeqDirectly",
|
||||
"isSubmitCfmSeqEventually",
|
||||
"isSubmitDurationData",
|
||||
"isSubmitCfmPtEteStart",
|
||||
"isSubmitCfmPtEteEnd",
|
||||
"isSubmitCfmPtEteSE",
|
||||
"isSubmitCfmPtPStart",
|
||||
"isSubmitCfmPtPEnd",
|
||||
"isSubmitCfmPtPSE",
|
||||
"isSubmitCfmWtEteStart",
|
||||
"isSubmitCfmWtEteEnd",
|
||||
"isSubmitCfmWtEteSE",
|
||||
"isSubmitCfmWtPStart",
|
||||
"isSubmitCfmWtPEnd",
|
||||
"isSubmitCfmWtPSE",
|
||||
"isSubmitCfmCtEteStart",
|
||||
"isSubmitCfmCtEteEnd",
|
||||
"isSubmitCfmCtEteSE",
|
||||
"isSubmitShowDataSeq",
|
||||
"isSubmitShowDataPtEte",
|
||||
"isSubmitShowDataPtP",
|
||||
"isSubmitShowDataWtEte",
|
||||
"isSubmitShowDataWtP",
|
||||
"isSubmitShowDataCt",
|
||||
]);
|
||||
|
||||
const task = ref(null);
|
||||
@@ -143,112 +433,166 @@ const taskEnd = ref(null);
|
||||
|
||||
// Activity sequence
|
||||
const cfmSeqStartData = computed(() => {
|
||||
if(props.isSubmit && task.value === null) task.value = props.isSubmitShowDataSeq.task;
|
||||
return isEndSelected.value ? setSeqStartAndEndData(cfmSeqEnd.value, 'sources', task.value) : cfmSeqStart.value.map(i => i.label);
|
||||
if (props.isSubmit && task.value === null)
|
||||
task.value = props.isSubmitShowDataSeq.task;
|
||||
return isEndSelected.value
|
||||
? setSeqStartAndEndData(cfmSeqEnd.value, "sources", task.value)
|
||||
: cfmSeqStart.value.map((i) => i.label);
|
||||
});
|
||||
const cfmSeqEndData = computed(() => {
|
||||
if(props.isSubmit && task.value === null) task.value = props.isSubmitShowDataSeq.task;
|
||||
return isStartSelected.value ? setSeqStartAndEndData(cfmSeqStart.value, 'sinks', task.value) : cfmSeqEnd.value.map(i => i.label);
|
||||
if (props.isSubmit && task.value === null)
|
||||
task.value = props.isSubmitShowDataSeq.task;
|
||||
return isStartSelected.value
|
||||
? setSeqStartAndEndData(cfmSeqStart.value, "sinks", task.value)
|
||||
: cfmSeqEnd.value.map((i) => i.label);
|
||||
});
|
||||
// Processing time
|
||||
const cfmPtEteStartData = computed(() => {
|
||||
return cfmPtEteStart.value.map(i => i.task);
|
||||
return cfmPtEteStart.value.map((i) => i.task);
|
||||
});
|
||||
const cfmPtEteEndData = computed(() => {
|
||||
return cfmPtEteEnd.value.map(i => i.task);
|
||||
return cfmPtEteEnd.value.map((i) => i.task);
|
||||
});
|
||||
const cfmPtEteSEStartData = computed(() => {
|
||||
if(props.isSubmit && task.value === null) task.value = props.isSubmitShowDataPtEte.task;
|
||||
return isEndSelected.value ? setStartAndEndData(cfmPtEteSE.value, 'end', task.value) : setTaskData(cfmPtEteSE.value, 'start');
|
||||
if (props.isSubmit && task.value === null)
|
||||
task.value = props.isSubmitShowDataPtEte.task;
|
||||
return isEndSelected.value
|
||||
? setStartAndEndData(cfmPtEteSE.value, "end", task.value)
|
||||
: setTaskData(cfmPtEteSE.value, "start");
|
||||
});
|
||||
const cfmPtEteSEEndData = computed(() => {
|
||||
if(props.isSubmit && task.value === null) task.value = props.isSubmitShowDataPtEte.task;
|
||||
return isStartSelected.value ? setStartAndEndData(cfmPtEteSE.value, 'start', task.value) : setTaskData(cfmPtEteSE.value, 'end');
|
||||
if (props.isSubmit && task.value === null)
|
||||
task.value = props.isSubmitShowDataPtEte.task;
|
||||
return isStartSelected.value
|
||||
? setStartAndEndData(cfmPtEteSE.value, "start", task.value)
|
||||
: setTaskData(cfmPtEteSE.value, "end");
|
||||
});
|
||||
const cfmPtPStartData = computed(() => {
|
||||
return cfmPtPStart.value.map(i => i.task);
|
||||
return cfmPtPStart.value.map((i) => i.task);
|
||||
});
|
||||
const cfmPtPEndData = computed(() => {
|
||||
return cfmPtPEnd.value.map(i => i.task);
|
||||
return cfmPtPEnd.value.map((i) => i.task);
|
||||
});
|
||||
const cfmPtPSEStartData = computed(() => {
|
||||
if(props.isSubmit && task.value === null) task.value = props.isSubmitShowDataPtP.task;
|
||||
return isEndSelected.value ? setStartAndEndData(cfmPtPSE.value, 'end', task.value) : setTaskData(cfmPtPSE.value, 'start');
|
||||
if (props.isSubmit && task.value === null)
|
||||
task.value = props.isSubmitShowDataPtP.task;
|
||||
return isEndSelected.value
|
||||
? setStartAndEndData(cfmPtPSE.value, "end", task.value)
|
||||
: setTaskData(cfmPtPSE.value, "start");
|
||||
});
|
||||
const cfmPtPSEEndData = computed(() => {
|
||||
if(props.isSubmit && task.value === null) task.value = props.isSubmitShowDataPtP.task;
|
||||
return isStartSelected.value ? setStartAndEndData(cfmPtPSE.value, 'start', task.value) : setTaskData(cfmPtPSE.value, 'end');
|
||||
if (props.isSubmit && task.value === null)
|
||||
task.value = props.isSubmitShowDataPtP.task;
|
||||
return isStartSelected.value
|
||||
? setStartAndEndData(cfmPtPSE.value, "start", task.value)
|
||||
: setTaskData(cfmPtPSE.value, "end");
|
||||
});
|
||||
// Waiting time
|
||||
const cfmWtEteStartData = computed(() => {
|
||||
return cfmWtEteStart.value.map(i => i.task);
|
||||
return cfmWtEteStart.value.map((i) => i.task);
|
||||
});
|
||||
const cfmWtEteEndData = computed(() => {
|
||||
return cfmWtEteEnd.value.map(i => i.task);
|
||||
return cfmWtEteEnd.value.map((i) => i.task);
|
||||
});
|
||||
const cfmWtEteSEStartData = computed(() => {
|
||||
if(props.isSubmit && task.value === null) task.value = props.isSubmitShowDataWtEte.task;
|
||||
return isEndSelected.value ? setStartAndEndData(cfmWtEteSE.value, 'end', task.value) : setTaskData(cfmWtEteSE.value, 'start');
|
||||
if (props.isSubmit && task.value === null)
|
||||
task.value = props.isSubmitShowDataWtEte.task;
|
||||
return isEndSelected.value
|
||||
? setStartAndEndData(cfmWtEteSE.value, "end", task.value)
|
||||
: setTaskData(cfmWtEteSE.value, "start");
|
||||
});
|
||||
const cfmWtEteSEEndData = computed(() => {
|
||||
if(props.isSubmit && task.value === null) task.value = props.isSubmitShowDataWtEte.task;
|
||||
return isStartSelected.value ? setStartAndEndData(cfmWtEteSE.value, 'start', task.value) : setTaskData(cfmWtEteSE.value, 'end');
|
||||
if (props.isSubmit && task.value === null)
|
||||
task.value = props.isSubmitShowDataWtEte.task;
|
||||
return isStartSelected.value
|
||||
? setStartAndEndData(cfmWtEteSE.value, "start", task.value)
|
||||
: setTaskData(cfmWtEteSE.value, "end");
|
||||
});
|
||||
const cfmWtPStartData = computed(() => {
|
||||
return cfmWtPStart.value.map(i => i.task);
|
||||
return cfmWtPStart.value.map((i) => i.task);
|
||||
});
|
||||
const cfmWtPEndData = computed(() => {
|
||||
return cfmWtPEnd.value.map(i => i.task);
|
||||
return cfmWtPEnd.value.map((i) => i.task);
|
||||
});
|
||||
const cfmWtPSEStartData = computed(() => {
|
||||
if(props.isSubmit && task.value === null) task.value = props.isSubmitShowDataWtP.task;
|
||||
return isEndSelected.value ? setStartAndEndData(cfmWtPSE.value, 'end', task.value) : setTaskData(cfmWtPSE.value, 'start');
|
||||
if (props.isSubmit && task.value === null)
|
||||
task.value = props.isSubmitShowDataWtP.task;
|
||||
return isEndSelected.value
|
||||
? setStartAndEndData(cfmWtPSE.value, "end", task.value)
|
||||
: setTaskData(cfmWtPSE.value, "start");
|
||||
});
|
||||
const cfmWtPSEEndData = computed(() => {
|
||||
if(props.isSubmit && task.value === null) task.value = props.isSubmitShowDataWtP.task;
|
||||
return isStartSelected.value ? setStartAndEndData(cfmWtPSE.value, 'start', task.value) : setTaskData(cfmWtPSE.value, 'end');
|
||||
if (props.isSubmit && task.value === null)
|
||||
task.value = props.isSubmitShowDataWtP.task;
|
||||
return isStartSelected.value
|
||||
? setStartAndEndData(cfmWtPSE.value, "start", task.value)
|
||||
: setTaskData(cfmWtPSE.value, "end");
|
||||
});
|
||||
// Cycle time
|
||||
const cfmCtEteStartData = computed(() => {
|
||||
return cfmCtEteStart.value.map(i => i.task);
|
||||
return cfmCtEteStart.value.map((i) => i.task);
|
||||
});
|
||||
const cfmCtEteEndData = computed(() => {
|
||||
return cfmCtEteEnd.value.map(i => i.task);
|
||||
return cfmCtEteEnd.value.map((i) => i.task);
|
||||
});
|
||||
const cfmCtEteSEStartData = computed(() => {
|
||||
if(props.isSubmit && task.value === null) task.value = props.isSubmitShowDataCt.task;
|
||||
return isEndSelected.value ? setStartAndEndData(cfmCtEteSE.value, 'end', task.value) : setTaskData(cfmCtEteSE.value, 'start');
|
||||
if (props.isSubmit && task.value === null)
|
||||
task.value = props.isSubmitShowDataCt.task;
|
||||
return isEndSelected.value
|
||||
? setStartAndEndData(cfmCtEteSE.value, "end", task.value)
|
||||
: setTaskData(cfmCtEteSE.value, "start");
|
||||
});
|
||||
const cfmCtEteSEEndData = computed(() => {
|
||||
if(props.isSubmit && task.value === null) task.value = props.isSubmitShowDataCt.task;
|
||||
return isStartSelected.value ? setStartAndEndData(cfmCtEteSE.value, 'start', task.value) : setTaskData(cfmCtEteSE.value, 'end');
|
||||
if (props.isSubmit && task.value === null)
|
||||
task.value = props.isSubmitShowDataCt.task;
|
||||
return isStartSelected.value
|
||||
? setStartAndEndData(cfmCtEteSE.value, "start", task.value)
|
||||
: setTaskData(cfmCtEteSE.value, "end");
|
||||
});
|
||||
|
||||
// Watchers - Fix issue where saved rule files could not be re-edited
|
||||
watch(() => props.isSubmitShowDataSeq, (newValue) => {
|
||||
taskStart.value = newValue.taskStart;
|
||||
taskEnd.value = newValue.taskEnd;
|
||||
});
|
||||
watch(() => props.isSubmitShowDataPtEte, (newValue) => {
|
||||
taskStart.value = newValue.taskStart;
|
||||
taskEnd.value = newValue.taskEnd;
|
||||
});
|
||||
watch(() => props.isSubmitShowDataPtP, (newValue) => {
|
||||
taskStart.value = newValue.taskStart;
|
||||
taskEnd.value = newValue.taskEnd;
|
||||
});
|
||||
watch(() => props.isSubmitShowDataWtEte, (newValue) => {
|
||||
taskStart.value = newValue.taskStart;
|
||||
taskEnd.value = newValue.taskEnd;
|
||||
});
|
||||
watch(() => props.isSubmitShowDataWtP, (newValue) => {
|
||||
taskStart.value = newValue.taskStart;
|
||||
taskEnd.value = newValue.taskEnd;
|
||||
});
|
||||
watch(() => props.isSubmitShowDataCt, (newValue) => {
|
||||
taskStart.value = newValue.taskStart;
|
||||
taskEnd.value = newValue.taskEnd;
|
||||
});
|
||||
watch(
|
||||
() => props.isSubmitShowDataSeq,
|
||||
(newValue) => {
|
||||
taskStart.value = newValue.taskStart;
|
||||
taskEnd.value = newValue.taskEnd;
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => props.isSubmitShowDataPtEte,
|
||||
(newValue) => {
|
||||
taskStart.value = newValue.taskStart;
|
||||
taskEnd.value = newValue.taskEnd;
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => props.isSubmitShowDataPtP,
|
||||
(newValue) => {
|
||||
taskStart.value = newValue.taskStart;
|
||||
taskEnd.value = newValue.taskEnd;
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => props.isSubmitShowDataWtEte,
|
||||
(newValue) => {
|
||||
taskStart.value = newValue.taskStart;
|
||||
taskEnd.value = newValue.taskEnd;
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => props.isSubmitShowDataWtP,
|
||||
(newValue) => {
|
||||
taskStart.value = newValue.taskStart;
|
||||
taskEnd.value = newValue.taskEnd;
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => props.isSubmitShowDataCt,
|
||||
(newValue) => {
|
||||
taskStart.value = newValue.taskStart;
|
||||
taskEnd.value = newValue.taskEnd;
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets the start and end radio data.
|
||||
@@ -257,7 +601,7 @@ watch(() => props.isSubmitShowDataCt, (newValue) => {
|
||||
* @returns {array}
|
||||
*/
|
||||
function setTaskData(data, category) {
|
||||
let newData = data.map(i => i[category]);
|
||||
let newData = data.map((i) => i[category]);
|
||||
newData = [...new Set(newData)]; // Set is a collection type that only stores unique values.
|
||||
return newData;
|
||||
}
|
||||
@@ -269,13 +613,15 @@ function setTaskData(data, category) {
|
||||
* @returns {array}
|
||||
*/
|
||||
function setStartAndEndData(data, category, taskVal) {
|
||||
let oppositeCategory = '';
|
||||
if (category === 'start') {
|
||||
oppositeCategory = 'end';
|
||||
let oppositeCategory = "";
|
||||
if (category === "start") {
|
||||
oppositeCategory = "end";
|
||||
} else {
|
||||
oppositeCategory = 'start';
|
||||
};
|
||||
let newData = data.filter(i => i[category] === taskVal).map(i => i[oppositeCategory]);
|
||||
oppositeCategory = "start";
|
||||
}
|
||||
let newData = data
|
||||
.filter((i) => i[category] === taskVal)
|
||||
.map((i) => i[oppositeCategory]);
|
||||
newData = [...new Set(newData)];
|
||||
return newData;
|
||||
}
|
||||
@@ -287,7 +633,7 @@ function setStartAndEndData(data, category, taskVal) {
|
||||
* @returns {array}
|
||||
*/
|
||||
function setSeqStartAndEndData(data, category, taskVal) {
|
||||
let newData = data.filter(i => i.label === taskVal).map(i => i[category]);
|
||||
let newData = data.filter((i) => i.label === taskVal).map((i) => i[category]);
|
||||
newData = [...new Set(...newData)];
|
||||
return newData;
|
||||
}
|
||||
@@ -297,16 +643,16 @@ function setSeqStartAndEndData(data, category, taskVal) {
|
||||
*/
|
||||
function selectStart(e) {
|
||||
taskStart.value = e;
|
||||
if(isStartSelected.value === null || isStartSelected.value === true){
|
||||
if (isStartSelected.value === null || isStartSelected.value === true) {
|
||||
isStartSelected.value = true;
|
||||
isEndSelected.value = false;
|
||||
task.value = e;
|
||||
taskEnd.value = null;
|
||||
emitter.emit('sratrAndEndToStart', {
|
||||
emitter.emit("sratrAndEndToStart", {
|
||||
start: true,
|
||||
end: false,
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* select End list's task
|
||||
@@ -314,12 +660,12 @@ function selectStart(e) {
|
||||
*/
|
||||
function selectEnd(e) {
|
||||
taskEnd.value = e;
|
||||
if(isEndSelected.value === null || isEndSelected.value === true){
|
||||
if (isEndSelected.value === null || isEndSelected.value === true) {
|
||||
isEndSelected.value = true;
|
||||
isStartSelected.value = false;
|
||||
task.value = e;
|
||||
taskStart.value = null;
|
||||
emitter.emit('sratrAndEndToStart', {
|
||||
emitter.emit("sratrAndEndToStart", {
|
||||
start: false,
|
||||
end: true,
|
||||
});
|
||||
@@ -340,22 +686,23 @@ function reset() {
|
||||
* @param {boolean} data - Whether data should be restored from submission state.
|
||||
*/
|
||||
function setResetData(data) {
|
||||
if(data) {
|
||||
if(props.isSubmit) {
|
||||
if (data) {
|
||||
if (props.isSubmit) {
|
||||
switch (selectedRuleType.value) {
|
||||
case 'Activity sequence':
|
||||
case "Activity sequence":
|
||||
task.value = props.isSubmitShowDataSeq.task;
|
||||
isStartSelected.value = props.isSubmitShowDataSeq.isStartSelected;
|
||||
isEndSelected.value = props.isSubmitShowDataSeq.isEndSelected;
|
||||
break;
|
||||
case 'Processing time':
|
||||
case "Processing time":
|
||||
switch (selectedProcessScope.value) {
|
||||
case 'End to end':
|
||||
case "End to end":
|
||||
task.value = props.isSubmitShowDataPtEte.task;
|
||||
isStartSelected.value = props.isSubmitShowDataPtEte.isStartSelected;
|
||||
isStartSelected.value =
|
||||
props.isSubmitShowDataPtEte.isStartSelected;
|
||||
isEndSelected.value = props.isSubmitShowDataPtEte.isEndSelected;
|
||||
break;
|
||||
case 'Partial':
|
||||
case "Partial":
|
||||
task.value = props.isSubmitShowDataPtP.task;
|
||||
isStartSelected.value = props.isSubmitShowDataPtP.isStartSelected;
|
||||
isEndSelected.value = props.isSubmitShowDataPtP.isEndSelected;
|
||||
@@ -364,23 +711,24 @@ function setResetData(data) {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'Waiting time':
|
||||
case "Waiting time":
|
||||
switch (selectedProcessScope.value) {
|
||||
case 'End to end':
|
||||
task.value = props.isSubmitShowDataWtEte.task;
|
||||
isStartSelected.value = props.isSubmitShowDataWtEte.isStartSelected;
|
||||
isEndSelected.value = props.isSubmitShowDataWtEte.isEndSelected;
|
||||
break;
|
||||
case 'Partial':
|
||||
task.value = props.isSubmitShowDataWtP.task;
|
||||
isStartSelected.value = props.isSubmitShowDataWtP.isStartSelected;
|
||||
isEndSelected.value = props.isSubmitShowDataWtP.isEndSelected;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'Cycle time':
|
||||
case "End to end":
|
||||
task.value = props.isSubmitShowDataWtEte.task;
|
||||
isStartSelected.value =
|
||||
props.isSubmitShowDataWtEte.isStartSelected;
|
||||
isEndSelected.value = props.isSubmitShowDataWtEte.isEndSelected;
|
||||
break;
|
||||
case "Partial":
|
||||
task.value = props.isSubmitShowDataWtP.task;
|
||||
isStartSelected.value = props.isSubmitShowDataWtP.isStartSelected;
|
||||
isEndSelected.value = props.isSubmitShowDataWtP.isEndSelected;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "Cycle time":
|
||||
task.value = props.isSubmitShowDataCt.task;
|
||||
isStartSelected.value = props.isSubmitShowDataCt.isStartSelected;
|
||||
isEndSelected.value = props.isSubmitShowDataCt.isEndSelected;
|
||||
@@ -395,28 +743,28 @@ function setResetData(data) {
|
||||
}
|
||||
|
||||
// created() logic
|
||||
emitter.on('isRadioChange', (data) => {
|
||||
emitter.on("isRadioChange", (data) => {
|
||||
setResetData(data);
|
||||
});
|
||||
emitter.on('isRadioSeqChange', (data) => {
|
||||
emitter.on("isRadioSeqChange", (data) => {
|
||||
setResetData(data);
|
||||
});
|
||||
emitter.on('isRadioProcessScopeChange', (data) => {
|
||||
if(data) {
|
||||
emitter.on("isRadioProcessScopeChange", (data) => {
|
||||
if (data) {
|
||||
setResetData(data);
|
||||
};
|
||||
}
|
||||
});
|
||||
emitter.on('isRadioActSeqMoreChange', (data) => {
|
||||
if(data) {
|
||||
emitter.on("isRadioActSeqMoreChange", (data) => {
|
||||
if (data) {
|
||||
setResetData(data);
|
||||
};
|
||||
}
|
||||
});
|
||||
emitter.on('isRadioActSeqFromToChange', (data) => {
|
||||
if(data) {
|
||||
emitter.on("isRadioActSeqFromToChange", (data) => {
|
||||
if (data) {
|
||||
setResetData(data);
|
||||
};
|
||||
}
|
||||
});
|
||||
emitter.on('reset', data => {
|
||||
emitter.on("reset", (data) => {
|
||||
reset();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,71 +1,226 @@
|
||||
<template>
|
||||
<div class="mt-2 mb-12" v-if="selectedRuleType === 'Activity duration' || selectedRuleType === 'Waiting time'
|
||||
|| selectedRuleType === 'Processing time' || selectedRuleType === 'Cycle time'">
|
||||
<p class="h2">Time Range</p>
|
||||
<div class=" text-sm leading-normal">
|
||||
<div
|
||||
class="mt-2 mb-12"
|
||||
v-if="
|
||||
selectedRuleType === 'Activity duration' ||
|
||||
selectedRuleType === 'Waiting time' ||
|
||||
selectedRuleType === 'Processing time' ||
|
||||
selectedRuleType === 'Cycle time'
|
||||
"
|
||||
>
|
||||
<p class="h2">Time Range</p>
|
||||
<div class="text-sm leading-normal">
|
||||
<!-- Activity duration -->
|
||||
<TimeRangeDuration
|
||||
v-if="selectedRuleType === 'Activity duration'" :time="state.timeDuration" :select="isSubmitDurationTime" @min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds" />
|
||||
<!-- Processing time -->
|
||||
<TimeRangeDuration v-if="selectedRuleType === 'Processing time' && selectedProcessScope === 'End to end'
|
||||
&& selectedActSeqMore === 'All'" :time="state.timeCfmPtEteAll" :select="isSubmitTimeCfmPtEteAll" @min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds" />
|
||||
<TimeRangeDuration v-if="selectedRuleType === 'Processing time' && selectedProcessScope === 'End to end'
|
||||
&& selectedActSeqMore === 'Start'" :time="state.timeCfmPtEteStart" :select="isSubmitTimeCfmPtEteStart" @min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds" />
|
||||
<TimeRangeDuration v-if="selectedRuleType === 'Processing time' && selectedProcessScope === 'End to end'
|
||||
&& selectedActSeqMore === 'End'" :time="state.timeCfmPtEteEnd" :select="isSubmitTimeCfmPtEteEnd" @min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds" />
|
||||
<TimeRangeDuration v-if="selectedRuleType === 'Processing time' && selectedProcessScope === 'End to end'
|
||||
&& selectedActSeqMore === 'Start & End'" :time="state.timeCfmPtEteSE" :select="isSubmitTimeCfmPtEteSE" @min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds" />
|
||||
<TimeRangeDuration v-if="selectedRuleType === 'Processing time' && selectedProcessScope === 'Partial'
|
||||
&& selectedActSeqFromTo === 'From'" :time="state.timeCfmPtPStart" :select="isSubmitTimeCfmPtPStart" @min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds" />
|
||||
<TimeRangeDuration v-if="selectedRuleType === 'Processing time' && selectedProcessScope === 'Partial'
|
||||
&& selectedActSeqFromTo === 'To'" :time="state.timeCfmPtPEnd" :select="isSubmitTimeCfmPtPEnd" @min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds" />
|
||||
<TimeRangeDuration v-if="selectedRuleType === 'Processing time' && selectedProcessScope === 'Partial'
|
||||
&& selectedActSeqFromTo === 'From & To'" :time="state.timeCfmPtPSE" :select="isSubmitTimeCfmPtPSE" @min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds" />
|
||||
<!-- Waiting time -->
|
||||
<TimeRangeDuration v-if="selectedRuleType === 'Waiting time' && selectedProcessScope === 'End to end'
|
||||
&& selectedActSeqMore === 'All'" :time="state.timeCfmWtEteAll" :select="isSubmitTimeCfmWtEteAll" @min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds" />
|
||||
<TimeRangeDuration v-if="selectedRuleType === 'Waiting time' && selectedProcessScope === 'End to end'
|
||||
&& selectedActSeqMore === 'Start'" :time="state.timeCfmWtEteStart" :select="isSubmitTimeCfmWtEteStart" @min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds" />
|
||||
<TimeRangeDuration v-if="selectedRuleType === 'Waiting time' && selectedProcessScope === 'End to end'
|
||||
&& selectedActSeqMore === 'End'" :time="state.timeCfmWtEteEnd" :select="isSubmitTimeCfmWtEteEnd" @min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds" />
|
||||
<TimeRangeDuration v-if="selectedRuleType === 'Waiting time' && selectedProcessScope === 'End to end'
|
||||
&& selectedActSeqMore === 'Start & End'" :time="state.timeCfmWtEteSE" :select="isSubmitTimeCfmWtEteSE" @min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds" />
|
||||
<TimeRangeDuration v-if="selectedRuleType === 'Waiting time' && selectedProcessScope === 'Partial'
|
||||
&& selectedActSeqFromTo === 'From'" :time="state.timeCfmWtPStart" :select="isSubmitTimeCfmWtPStart" @min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds" />
|
||||
<TimeRangeDuration v-if="selectedRuleType === 'Waiting time' && selectedProcessScope === 'Partial'
|
||||
&& selectedActSeqFromTo === 'To'" :time="state.timeCfmWtPEnd" :select="isSubmitTimeCfmWtPEnd" @min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds" />
|
||||
<TimeRangeDuration v-if="selectedRuleType === 'Waiting time' && selectedProcessScope === 'Partial'
|
||||
&& selectedActSeqFromTo === 'From & To'" :time="state.timeCfmWtPSE" :select="isSubmitTimeCfmWtPSE" @min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds" />
|
||||
<!-- Cycle time -->
|
||||
<TimeRangeDuration v-if="selectedRuleType === 'Cycle time' && selectedProcessScope === 'End to end'
|
||||
&& selectedActSeqMore === 'All'" :time="state.timeCfmCtEteAll" :select="isSubmitTimeCfmCtEteAll" @min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds" />
|
||||
<TimeRangeDuration v-if="selectedRuleType === 'Cycle time' && selectedProcessScope === 'End to end'
|
||||
&& selectedActSeqMore === 'Start'" :time="state.timeCfmCtEteStart" :select="isSubmitTimeCfmCtEteStart" @min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds" />
|
||||
<TimeRangeDuration v-if="selectedRuleType === 'Cycle time' && selectedProcessScope === 'End to end'
|
||||
&& selectedActSeqMore === 'End'" :time="state.timeCfmCtEteEnd" :select="isSubmitTimeCfmCtEteEnd" @min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds" />
|
||||
<TimeRangeDuration v-if="selectedRuleType === 'Cycle time' && selectedProcessScope === 'End to end'
|
||||
&& selectedActSeqMore === 'Start & End'" :time="state.timeCfmCtEteSE" :select="isSubmitTimeCfmCtEteSE" @min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds" />
|
||||
v-if="selectedRuleType === 'Activity duration'"
|
||||
:time="state.timeDuration"
|
||||
:select="isSubmitDurationTime"
|
||||
@min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds"
|
||||
/>
|
||||
<!-- Processing time -->
|
||||
<TimeRangeDuration
|
||||
v-if="
|
||||
selectedRuleType === 'Processing time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'All'
|
||||
"
|
||||
:time="state.timeCfmPtEteAll"
|
||||
:select="isSubmitTimeCfmPtEteAll"
|
||||
@min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds"
|
||||
/>
|
||||
<TimeRangeDuration
|
||||
v-if="
|
||||
selectedRuleType === 'Processing time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'Start'
|
||||
"
|
||||
:time="state.timeCfmPtEteStart"
|
||||
:select="isSubmitTimeCfmPtEteStart"
|
||||
@min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds"
|
||||
/>
|
||||
<TimeRangeDuration
|
||||
v-if="
|
||||
selectedRuleType === 'Processing time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'End'
|
||||
"
|
||||
:time="state.timeCfmPtEteEnd"
|
||||
:select="isSubmitTimeCfmPtEteEnd"
|
||||
@min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds"
|
||||
/>
|
||||
<TimeRangeDuration
|
||||
v-if="
|
||||
selectedRuleType === 'Processing time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'Start & End'
|
||||
"
|
||||
:time="state.timeCfmPtEteSE"
|
||||
:select="isSubmitTimeCfmPtEteSE"
|
||||
@min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds"
|
||||
/>
|
||||
<TimeRangeDuration
|
||||
v-if="
|
||||
selectedRuleType === 'Processing time' &&
|
||||
selectedProcessScope === 'Partial' &&
|
||||
selectedActSeqFromTo === 'From'
|
||||
"
|
||||
:time="state.timeCfmPtPStart"
|
||||
:select="isSubmitTimeCfmPtPStart"
|
||||
@min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds"
|
||||
/>
|
||||
<TimeRangeDuration
|
||||
v-if="
|
||||
selectedRuleType === 'Processing time' &&
|
||||
selectedProcessScope === 'Partial' &&
|
||||
selectedActSeqFromTo === 'To'
|
||||
"
|
||||
:time="state.timeCfmPtPEnd"
|
||||
:select="isSubmitTimeCfmPtPEnd"
|
||||
@min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds"
|
||||
/>
|
||||
<TimeRangeDuration
|
||||
v-if="
|
||||
selectedRuleType === 'Processing time' &&
|
||||
selectedProcessScope === 'Partial' &&
|
||||
selectedActSeqFromTo === 'From & To'
|
||||
"
|
||||
:time="state.timeCfmPtPSE"
|
||||
:select="isSubmitTimeCfmPtPSE"
|
||||
@min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds"
|
||||
/>
|
||||
<!-- Waiting time -->
|
||||
<TimeRangeDuration
|
||||
v-if="
|
||||
selectedRuleType === 'Waiting time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'All'
|
||||
"
|
||||
:time="state.timeCfmWtEteAll"
|
||||
:select="isSubmitTimeCfmWtEteAll"
|
||||
@min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds"
|
||||
/>
|
||||
<TimeRangeDuration
|
||||
v-if="
|
||||
selectedRuleType === 'Waiting time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'Start'
|
||||
"
|
||||
:time="state.timeCfmWtEteStart"
|
||||
:select="isSubmitTimeCfmWtEteStart"
|
||||
@min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds"
|
||||
/>
|
||||
<TimeRangeDuration
|
||||
v-if="
|
||||
selectedRuleType === 'Waiting time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'End'
|
||||
"
|
||||
:time="state.timeCfmWtEteEnd"
|
||||
:select="isSubmitTimeCfmWtEteEnd"
|
||||
@min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds"
|
||||
/>
|
||||
<TimeRangeDuration
|
||||
v-if="
|
||||
selectedRuleType === 'Waiting time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'Start & End'
|
||||
"
|
||||
:time="state.timeCfmWtEteSE"
|
||||
:select="isSubmitTimeCfmWtEteSE"
|
||||
@min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds"
|
||||
/>
|
||||
<TimeRangeDuration
|
||||
v-if="
|
||||
selectedRuleType === 'Waiting time' &&
|
||||
selectedProcessScope === 'Partial' &&
|
||||
selectedActSeqFromTo === 'From'
|
||||
"
|
||||
:time="state.timeCfmWtPStart"
|
||||
:select="isSubmitTimeCfmWtPStart"
|
||||
@min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds"
|
||||
/>
|
||||
<TimeRangeDuration
|
||||
v-if="
|
||||
selectedRuleType === 'Waiting time' &&
|
||||
selectedProcessScope === 'Partial' &&
|
||||
selectedActSeqFromTo === 'To'
|
||||
"
|
||||
:time="state.timeCfmWtPEnd"
|
||||
:select="isSubmitTimeCfmWtPEnd"
|
||||
@min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds"
|
||||
/>
|
||||
<TimeRangeDuration
|
||||
v-if="
|
||||
selectedRuleType === 'Waiting time' &&
|
||||
selectedProcessScope === 'Partial' &&
|
||||
selectedActSeqFromTo === 'From & To'
|
||||
"
|
||||
:time="state.timeCfmWtPSE"
|
||||
:select="isSubmitTimeCfmWtPSE"
|
||||
@min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds"
|
||||
/>
|
||||
<!-- Cycle time -->
|
||||
<TimeRangeDuration
|
||||
v-if="
|
||||
selectedRuleType === 'Cycle time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'All'
|
||||
"
|
||||
:time="state.timeCfmCtEteAll"
|
||||
:select="isSubmitTimeCfmCtEteAll"
|
||||
@min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds"
|
||||
/>
|
||||
<TimeRangeDuration
|
||||
v-if="
|
||||
selectedRuleType === 'Cycle time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'Start'
|
||||
"
|
||||
:time="state.timeCfmCtEteStart"
|
||||
:select="isSubmitTimeCfmCtEteStart"
|
||||
@min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds"
|
||||
/>
|
||||
<TimeRangeDuration
|
||||
v-if="
|
||||
selectedRuleType === 'Cycle time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'End'
|
||||
"
|
||||
:time="state.timeCfmCtEteEnd"
|
||||
:select="isSubmitTimeCfmCtEteEnd"
|
||||
@min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds"
|
||||
/>
|
||||
<TimeRangeDuration
|
||||
v-if="
|
||||
selectedRuleType === 'Cycle time' &&
|
||||
selectedProcessScope === 'End to end' &&
|
||||
selectedActSeqMore === 'Start & End'
|
||||
"
|
||||
:time="state.timeCfmCtEteSE"
|
||||
:select="isSubmitTimeCfmCtEteSE"
|
||||
@min-total-seconds="minTotalSeconds"
|
||||
@max-total-seconds="maxTotalSeconds"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
// The Lucia project.
|
||||
@@ -80,30 +235,67 @@
|
||||
* configuration with calendar inputs.
|
||||
*/
|
||||
|
||||
import { reactive } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useConformanceStore } from '@/stores/conformance';
|
||||
import emitter from '@/utils/emitter';
|
||||
import TimeRangeDuration from '@/components/Discover/Conformance/ConformanceSidebar/TimeRangeDuration.vue';
|
||||
import { reactive } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useConformanceStore } from "@/stores/conformance";
|
||||
import emitter from "@/utils/emitter";
|
||||
import TimeRangeDuration from "@/components/Discover/Conformance/ConformanceSidebar/TimeRangeDuration.vue";
|
||||
|
||||
const conformanceStore = useConformanceStore();
|
||||
const { selectedRuleType, selectedActivitySequence, selectedMode, selectedProcessScope,
|
||||
selectedActSeqMore, selectedActSeqFromTo, conformanceAllTasks, conformanceTask,
|
||||
cfmSeqStart, cfmSeqEnd, cfmPtEteStart, cfmPtEteEnd, cfmPtEteSE, cfmPtPStart,
|
||||
cfmPtPEnd, cfmPtPSE, cfmWtEteStart, cfmWtEteEnd, cfmWtEteSE, cfmWtPStart,
|
||||
cfmWtPEnd, cfmWtPSE, cfmCtEteStart, cfmCtEteEnd, cfmCtEteSE, cfmPtEteWhole,
|
||||
cfmWtEteWhole, cfmCtEteWhole
|
||||
const {
|
||||
selectedRuleType,
|
||||
selectedActivitySequence,
|
||||
selectedMode,
|
||||
selectedProcessScope,
|
||||
selectedActSeqMore,
|
||||
selectedActSeqFromTo,
|
||||
conformanceAllTasks,
|
||||
conformanceTask,
|
||||
cfmSeqStart,
|
||||
cfmSeqEnd,
|
||||
cfmPtEteStart,
|
||||
cfmPtEteEnd,
|
||||
cfmPtEteSE,
|
||||
cfmPtPStart,
|
||||
cfmPtPEnd,
|
||||
cfmPtPSE,
|
||||
cfmWtEteStart,
|
||||
cfmWtEteEnd,
|
||||
cfmWtEteSE,
|
||||
cfmWtPStart,
|
||||
cfmWtPEnd,
|
||||
cfmWtPSE,
|
||||
cfmCtEteStart,
|
||||
cfmCtEteEnd,
|
||||
cfmCtEteSE,
|
||||
cfmPtEteWhole,
|
||||
cfmWtEteWhole,
|
||||
cfmCtEteWhole,
|
||||
} = storeToRefs(conformanceStore);
|
||||
|
||||
const props = defineProps(['isSubmitDurationTime', 'isSubmitTimeCfmPtEteAll', 'isSubmitTimeCfmPtEteStart',
|
||||
'isSubmitTimeCfmPtEteEnd', 'isSubmitTimeCfmPtEteSE', 'isSubmitTimeCfmPtPStart',
|
||||
'isSubmitTimeCfmPtPEnd', 'isSubmitTimeCfmPtPSE', 'isSubmitTimeCfmWtEteAll',
|
||||
'isSubmitTimeCfmWtEteStart', 'isSubmitTimeCfmWtEteEnd', 'isSubmitTimeCfmWtEteSE',
|
||||
'isSubmitTimeCfmWtPStart', 'isSubmitTimeCfmWtPEnd', 'isSubmitTimeCfmWtPSE', 'isSubmitTimeCfmCtEteAll',
|
||||
'isSubmitTimeCfmCtEteStart', 'isSubmitTimeCfmCtEteEnd', 'isSubmitTimeCfmCtEteSE'
|
||||
const props = defineProps([
|
||||
"isSubmitDurationTime",
|
||||
"isSubmitTimeCfmPtEteAll",
|
||||
"isSubmitTimeCfmPtEteStart",
|
||||
"isSubmitTimeCfmPtEteEnd",
|
||||
"isSubmitTimeCfmPtEteSE",
|
||||
"isSubmitTimeCfmPtPStart",
|
||||
"isSubmitTimeCfmPtPEnd",
|
||||
"isSubmitTimeCfmPtPSE",
|
||||
"isSubmitTimeCfmWtEteAll",
|
||||
"isSubmitTimeCfmWtEteStart",
|
||||
"isSubmitTimeCfmWtEteEnd",
|
||||
"isSubmitTimeCfmWtEteSE",
|
||||
"isSubmitTimeCfmWtPStart",
|
||||
"isSubmitTimeCfmWtPEnd",
|
||||
"isSubmitTimeCfmWtPSE",
|
||||
"isSubmitTimeCfmCtEteAll",
|
||||
"isSubmitTimeCfmCtEteStart",
|
||||
"isSubmitTimeCfmCtEteEnd",
|
||||
"isSubmitTimeCfmCtEteSE",
|
||||
]);
|
||||
|
||||
const emit = defineEmits(['min-total-seconds', 'max-total-seconds']);
|
||||
const emit = defineEmits(["min-total-seconds", "max-total-seconds"]);
|
||||
|
||||
const state = reactive({
|
||||
timeDuration: null, // Activity duration
|
||||
@@ -164,14 +356,14 @@ const storeRefs = {
|
||||
* @param {number} e - The minimum total seconds.
|
||||
*/
|
||||
function minTotalSeconds(e) {
|
||||
emit('min-total-seconds', e);
|
||||
emit("min-total-seconds", e);
|
||||
}
|
||||
/**
|
||||
* get min total seconds
|
||||
* @param {number} e - The maximum total seconds.
|
||||
*/
|
||||
function maxTotalSeconds(e) {
|
||||
emit('max-total-seconds', e);
|
||||
emit("max-total-seconds", e);
|
||||
}
|
||||
/**
|
||||
* get Time Range(duration)
|
||||
@@ -182,35 +374,35 @@ function maxTotalSeconds(e) {
|
||||
* @returns {object} {min:12, max:345}
|
||||
*/
|
||||
function getDurationTime(data, category, task, taskTwo) {
|
||||
let result = {min:0, max:0};
|
||||
let result = { min: 0, max: 0 };
|
||||
switch (category) {
|
||||
case 'act':
|
||||
data.forEach(i => {
|
||||
if(i.label === task) {
|
||||
case "act":
|
||||
data.forEach((i) => {
|
||||
if (i.label === task) {
|
||||
result = i.duration;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'single':
|
||||
data.forEach(i => {
|
||||
if(i.task === task) {
|
||||
case "single":
|
||||
data.forEach((i) => {
|
||||
if (i.task === task) {
|
||||
result = i.time;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'double':
|
||||
data.forEach(i => {
|
||||
if(i.start === task && i.end === taskTwo) {
|
||||
case "double":
|
||||
data.forEach((i) => {
|
||||
if (i.start === task && i.end === taskTwo) {
|
||||
result = i.time;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'all':
|
||||
case "all":
|
||||
result = data;
|
||||
break
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
@@ -249,148 +441,235 @@ function reset() {
|
||||
}
|
||||
|
||||
// created() logic
|
||||
emitter.on('actRadioData', (data) => {
|
||||
emitter.on("actRadioData", (data) => {
|
||||
const category = data.category;
|
||||
const task = data.task;
|
||||
|
||||
const handleDoubleSelection = (startKey, endKey, timeKey, durationType) => {
|
||||
state[startKey] = task;
|
||||
state[timeKey] = { min: 0, max: 0 };
|
||||
if (state[endKey]) {
|
||||
state[timeKey] = getDurationTime(storeRefs[durationType].value, 'double', task, state[endKey]);
|
||||
}
|
||||
state[startKey] = task;
|
||||
state[timeKey] = { min: 0, max: 0 };
|
||||
if (state[endKey]) {
|
||||
state[timeKey] = getDurationTime(
|
||||
storeRefs[durationType].value,
|
||||
"double",
|
||||
task,
|
||||
state[endKey],
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSingleSelection = (key, timeKey, durationType) => {
|
||||
state[timeKey] = getDurationTime(storeRefs[durationType].value, 'single', task);
|
||||
state[timeKey] = getDurationTime(
|
||||
storeRefs[durationType].value,
|
||||
"single",
|
||||
task,
|
||||
);
|
||||
};
|
||||
|
||||
switch (category) {
|
||||
// Activity duration
|
||||
case 'cfmDur':
|
||||
state.timeDuration = getDurationTime(conformanceAllTasks.value, 'act', task);
|
||||
break;
|
||||
// Processing time
|
||||
case 'cfmPtEteStart':
|
||||
handleSingleSelection('cfmPtEteStart', 'timeCfmPtEteStart', 'cfmPtEteStart');
|
||||
break;
|
||||
case 'cfmPtEteEnd':
|
||||
handleSingleSelection('cfmPtEteEnd', 'timeCfmPtEteEnd', 'cfmPtEteEnd');
|
||||
break;
|
||||
case 'cfmPtEteSEStart':
|
||||
handleDoubleSelection('selectCfmPtEteSEStart', 'selectCfmPtEteSEEnd', 'timeCfmPtEteSE', 'cfmPtEteSE');
|
||||
break;
|
||||
case 'cfmPtEteSEEnd':
|
||||
handleDoubleSelection('selectCfmPtEteSEEnd', 'selectCfmPtEteSEStart', 'timeCfmPtEteSE', 'cfmPtEteSE');
|
||||
break;
|
||||
case 'cfmPtPStart':
|
||||
handleSingleSelection('cfmPtPStart', 'timeCfmPtPStart', 'cfmPtPStart');
|
||||
break;
|
||||
case 'cfmPtPEnd':
|
||||
handleSingleSelection('cfmPtPEnd', 'timeCfmPtPEnd', 'cfmPtPEnd');
|
||||
break;
|
||||
case 'cfmPtPSEStart':
|
||||
handleDoubleSelection('selectCfmPtPSEStart', 'selectCfmPtPSEEnd', 'timeCfmPtPSE', 'cfmPtPSE');
|
||||
break;
|
||||
case 'cfmPtPSEEnd':
|
||||
handleDoubleSelection('selectCfmPtPSEEnd', 'selectCfmPtPSEStart', 'timeCfmPtPSE', 'cfmPtPSE');
|
||||
break;
|
||||
// Waiting time
|
||||
case 'cfmWtEteStart':
|
||||
handleSingleSelection('cfmWtEteStart', 'timeCfmWtEteStart', 'cfmWtEteStart');
|
||||
break;
|
||||
case 'cfmWtEteEnd':
|
||||
handleSingleSelection('cfmWtEteEnd', 'timeCfmWtEteEnd', 'cfmWtEteEnd');
|
||||
break;
|
||||
case 'cfmWtEteSEStart':
|
||||
handleDoubleSelection('selectCfmWtEteSEStart', 'selectCfmWtEteSEEnd', 'timeCfmWtEteSE', 'cfmWtEteSE');
|
||||
break;
|
||||
case 'cfmWtEteSEEnd':
|
||||
handleDoubleSelection('selectCfmWtEteSEEnd', 'selectCfmWtEteSEStart', 'timeCfmWtEteSE', 'cfmWtEteSE');
|
||||
break;
|
||||
case 'cfmWtPStart':
|
||||
handleSingleSelection('cfmWtPStart', 'timeCfmWtPStart', 'cfmWtPStart');
|
||||
break;
|
||||
case 'cfmWtPEnd':
|
||||
handleSingleSelection('cfmWtPEnd', 'timeCfmWtPEnd', 'cfmWtPEnd');
|
||||
break;
|
||||
case 'cfmWtPSEStart':
|
||||
handleDoubleSelection('selectCfmWtPSEStart', 'selectCfmWtPSEEnd', 'timeCfmWtPSE', 'cfmWtPSE');
|
||||
break;
|
||||
case 'cfmWtPSEEnd':
|
||||
handleDoubleSelection('selectCfmWtPSEEnd', 'selectCfmWtPSEStart', 'timeCfmWtPSE', 'cfmWtPSE');
|
||||
break;
|
||||
// Cycle time
|
||||
case 'cfmCtEteStart':
|
||||
handleSingleSelection('cfmCtEteStart', 'timeCfmCtEteStart', 'cfmCtEteStart');
|
||||
break;
|
||||
case 'cfmCtEteEnd':
|
||||
handleSingleSelection('cfmCtEteEnd', 'timeCfmCtEteEnd', 'cfmCtEteEnd');
|
||||
break;
|
||||
case 'cfmCtEteSEStart':
|
||||
handleDoubleSelection('selectCfmCtEteSEStart', 'selectCfmCtEteSEEnd', 'timeCfmCtEteSE', 'cfmCtEteSE');
|
||||
break;
|
||||
case 'cfmCtEteSEEnd':
|
||||
handleDoubleSelection('selectCfmCtEteSEEnd', 'selectCfmCtEteSEStart', 'timeCfmCtEteSE', 'cfmCtEteSE');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
});
|
||||
emitter.on('reset', (data) => {
|
||||
reset();
|
||||
});
|
||||
emitter.on('isRadioChange', (data) => {
|
||||
if(data) {
|
||||
reset();
|
||||
switch (selectedRuleType.value) {
|
||||
case 'Processing time':
|
||||
state.timeCfmPtEteAll = getDurationTime(cfmPtEteWhole.value, 'all');
|
||||
state.timeCfmPtEteAllDefault = JSON.parse(JSON.stringify(state.timeCfmPtEteAll));
|
||||
break;
|
||||
case 'Waiting time':
|
||||
state.timeCfmWtEteAll = getDurationTime(cfmWtEteWhole.value, 'all');
|
||||
state.timeCfmWtEteAllDefault = JSON.parse(JSON.stringify(state.timeCfmWtEteAll));
|
||||
break;
|
||||
case 'Cycle time':
|
||||
state.timeCfmCtEteAll = getDurationTime(cfmCtEteWhole.value, 'all');
|
||||
state.timeCfmCtEteAllDefault = JSON.parse(JSON.stringify(state.timeCfmCtEteAll));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
// Activity duration
|
||||
case "cfmDur":
|
||||
state.timeDuration = getDurationTime(
|
||||
conformanceAllTasks.value,
|
||||
"act",
|
||||
task,
|
||||
);
|
||||
break;
|
||||
// Processing time
|
||||
case "cfmPtEteStart":
|
||||
handleSingleSelection(
|
||||
"cfmPtEteStart",
|
||||
"timeCfmPtEteStart",
|
||||
"cfmPtEteStart",
|
||||
);
|
||||
break;
|
||||
case "cfmPtEteEnd":
|
||||
handleSingleSelection("cfmPtEteEnd", "timeCfmPtEteEnd", "cfmPtEteEnd");
|
||||
break;
|
||||
case "cfmPtEteSEStart":
|
||||
handleDoubleSelection(
|
||||
"selectCfmPtEteSEStart",
|
||||
"selectCfmPtEteSEEnd",
|
||||
"timeCfmPtEteSE",
|
||||
"cfmPtEteSE",
|
||||
);
|
||||
break;
|
||||
case "cfmPtEteSEEnd":
|
||||
handleDoubleSelection(
|
||||
"selectCfmPtEteSEEnd",
|
||||
"selectCfmPtEteSEStart",
|
||||
"timeCfmPtEteSE",
|
||||
"cfmPtEteSE",
|
||||
);
|
||||
break;
|
||||
case "cfmPtPStart":
|
||||
handleSingleSelection("cfmPtPStart", "timeCfmPtPStart", "cfmPtPStart");
|
||||
break;
|
||||
case "cfmPtPEnd":
|
||||
handleSingleSelection("cfmPtPEnd", "timeCfmPtPEnd", "cfmPtPEnd");
|
||||
break;
|
||||
case "cfmPtPSEStart":
|
||||
handleDoubleSelection(
|
||||
"selectCfmPtPSEStart",
|
||||
"selectCfmPtPSEEnd",
|
||||
"timeCfmPtPSE",
|
||||
"cfmPtPSE",
|
||||
);
|
||||
break;
|
||||
case "cfmPtPSEEnd":
|
||||
handleDoubleSelection(
|
||||
"selectCfmPtPSEEnd",
|
||||
"selectCfmPtPSEStart",
|
||||
"timeCfmPtPSE",
|
||||
"cfmPtPSE",
|
||||
);
|
||||
break;
|
||||
// Waiting time
|
||||
case "cfmWtEteStart":
|
||||
handleSingleSelection(
|
||||
"cfmWtEteStart",
|
||||
"timeCfmWtEteStart",
|
||||
"cfmWtEteStart",
|
||||
);
|
||||
break;
|
||||
case "cfmWtEteEnd":
|
||||
handleSingleSelection("cfmWtEteEnd", "timeCfmWtEteEnd", "cfmWtEteEnd");
|
||||
break;
|
||||
case "cfmWtEteSEStart":
|
||||
handleDoubleSelection(
|
||||
"selectCfmWtEteSEStart",
|
||||
"selectCfmWtEteSEEnd",
|
||||
"timeCfmWtEteSE",
|
||||
"cfmWtEteSE",
|
||||
);
|
||||
break;
|
||||
case "cfmWtEteSEEnd":
|
||||
handleDoubleSelection(
|
||||
"selectCfmWtEteSEEnd",
|
||||
"selectCfmWtEteSEStart",
|
||||
"timeCfmWtEteSE",
|
||||
"cfmWtEteSE",
|
||||
);
|
||||
break;
|
||||
case "cfmWtPStart":
|
||||
handleSingleSelection("cfmWtPStart", "timeCfmWtPStart", "cfmWtPStart");
|
||||
break;
|
||||
case "cfmWtPEnd":
|
||||
handleSingleSelection("cfmWtPEnd", "timeCfmWtPEnd", "cfmWtPEnd");
|
||||
break;
|
||||
case "cfmWtPSEStart":
|
||||
handleDoubleSelection(
|
||||
"selectCfmWtPSEStart",
|
||||
"selectCfmWtPSEEnd",
|
||||
"timeCfmWtPSE",
|
||||
"cfmWtPSE",
|
||||
);
|
||||
break;
|
||||
case "cfmWtPSEEnd":
|
||||
handleDoubleSelection(
|
||||
"selectCfmWtPSEEnd",
|
||||
"selectCfmWtPSEStart",
|
||||
"timeCfmWtPSE",
|
||||
"cfmWtPSE",
|
||||
);
|
||||
break;
|
||||
// Cycle time
|
||||
case "cfmCtEteStart":
|
||||
handleSingleSelection(
|
||||
"cfmCtEteStart",
|
||||
"timeCfmCtEteStart",
|
||||
"cfmCtEteStart",
|
||||
);
|
||||
break;
|
||||
case "cfmCtEteEnd":
|
||||
handleSingleSelection("cfmCtEteEnd", "timeCfmCtEteEnd", "cfmCtEteEnd");
|
||||
break;
|
||||
case "cfmCtEteSEStart":
|
||||
handleDoubleSelection(
|
||||
"selectCfmCtEteSEStart",
|
||||
"selectCfmCtEteSEEnd",
|
||||
"timeCfmCtEteSE",
|
||||
"cfmCtEteSE",
|
||||
);
|
||||
break;
|
||||
case "cfmCtEteSEEnd":
|
||||
handleDoubleSelection(
|
||||
"selectCfmCtEteSEEnd",
|
||||
"selectCfmCtEteSEStart",
|
||||
"timeCfmCtEteSE",
|
||||
"cfmCtEteSE",
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
emitter.on('isRadioProcessScopeChange', (data) => {
|
||||
if(data) {
|
||||
reset();
|
||||
};
|
||||
emitter.on("reset", (data) => {
|
||||
reset();
|
||||
});
|
||||
emitter.on('isRadioActSeqMoreChange', (data) => {
|
||||
if(data) {
|
||||
if(selectedActSeqMore.value === 'All') {
|
||||
emitter.on("isRadioChange", (data) => {
|
||||
if (data) {
|
||||
reset();
|
||||
switch (selectedRuleType.value) {
|
||||
case "Processing time":
|
||||
state.timeCfmPtEteAll = getDurationTime(cfmPtEteWhole.value, "all");
|
||||
state.timeCfmPtEteAllDefault = JSON.parse(
|
||||
JSON.stringify(state.timeCfmPtEteAll),
|
||||
);
|
||||
break;
|
||||
case "Waiting time":
|
||||
state.timeCfmWtEteAll = getDurationTime(cfmWtEteWhole.value, "all");
|
||||
state.timeCfmWtEteAllDefault = JSON.parse(
|
||||
JSON.stringify(state.timeCfmWtEteAll),
|
||||
);
|
||||
break;
|
||||
case "Cycle time":
|
||||
state.timeCfmCtEteAll = getDurationTime(cfmCtEteWhole.value, "all");
|
||||
state.timeCfmCtEteAllDefault = JSON.parse(
|
||||
JSON.stringify(state.timeCfmCtEteAll),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
emitter.on("isRadioProcessScopeChange", (data) => {
|
||||
if (data) {
|
||||
reset();
|
||||
}
|
||||
});
|
||||
emitter.on("isRadioActSeqMoreChange", (data) => {
|
||||
if (data) {
|
||||
if (selectedActSeqMore.value === "All") {
|
||||
switch (selectedRuleType.value) {
|
||||
case 'Processing time':
|
||||
state.timeCfmPtEteAll = getDurationTime(cfmPtEteWhole.value, 'all');
|
||||
state.timeCfmPtEteAllDefault = JSON.parse(JSON.stringify(state.timeCfmPtEteAll));
|
||||
case "Processing time":
|
||||
state.timeCfmPtEteAll = getDurationTime(cfmPtEteWhole.value, "all");
|
||||
state.timeCfmPtEteAllDefault = JSON.parse(
|
||||
JSON.stringify(state.timeCfmPtEteAll),
|
||||
);
|
||||
break;
|
||||
case 'Waiting time':
|
||||
state.timeCfmWtEteAll = getDurationTime(cfmWtEteWhole.value, 'all');
|
||||
state.timeCfmWtEteAllDefault = JSON.parse(JSON.stringify(state.timeCfmWtEteAll));
|
||||
case "Waiting time":
|
||||
state.timeCfmWtEteAll = getDurationTime(cfmWtEteWhole.value, "all");
|
||||
state.timeCfmWtEteAllDefault = JSON.parse(
|
||||
JSON.stringify(state.timeCfmWtEteAll),
|
||||
);
|
||||
break;
|
||||
case 'Cycle time':
|
||||
state.timeCfmCtEteAll = getDurationTime(cfmCtEteWhole.value, 'all');
|
||||
state.timeCfmCtEteAllDefault = JSON.parse(JSON.stringify(state.timeCfmCtEteAll));
|
||||
case "Cycle time":
|
||||
state.timeCfmCtEteAll = getDurationTime(cfmCtEteWhole.value, "all");
|
||||
state.timeCfmCtEteAllDefault = JSON.parse(
|
||||
JSON.stringify(state.timeCfmCtEteAll),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}else reset();
|
||||
};
|
||||
}
|
||||
} else reset();
|
||||
}
|
||||
});
|
||||
emitter.on('isRadioActSeqFromToChange', (data) => {
|
||||
if(data) {
|
||||
emitter.on("isRadioActSeqFromToChange", (data) => {
|
||||
if (data) {
|
||||
reset();
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
<template>
|
||||
<ul class="space-y-2" id="cyp-conformance-result-arrow">
|
||||
<li class="flex justify-start items-center pr-4" v-for="(act, index) in data" :key="index" :title="act">
|
||||
<li
|
||||
class="flex justify-start items-center pr-4"
|
||||
v-for="(act, index) in data"
|
||||
:key="index"
|
||||
:title="act"
|
||||
>
|
||||
<span class="material-symbols-outlined text-primary mr-2">
|
||||
arrow_circle_down
|
||||
</span>
|
||||
<p class="px-2 py-1 border border-neutral-500 w-full whitespace-nowrap break-keep text-ellipsis overflow-hidden">{{ act }}</p>
|
||||
<p
|
||||
class="px-2 py-1 border border-neutral-500 w-full whitespace-nowrap break-keep text-ellipsis overflow-hidden"
|
||||
>
|
||||
{{ act }}
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
@@ -20,5 +29,5 @@
|
||||
* sequences.
|
||||
*/
|
||||
|
||||
defineProps(['data', 'select']);
|
||||
defineProps(["data", "select"]);
|
||||
</script>
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
<template>
|
||||
<ul class="space-y-2" id="cyp-conformance-result-check">
|
||||
<li class="flex justify-start items-center pr-4" v-for="(act, index) in datadata" :key="index" :title="act">
|
||||
<li
|
||||
class="flex justify-start items-center pr-4"
|
||||
v-for="(act, index) in datadata"
|
||||
:key="index"
|
||||
:title="act"
|
||||
>
|
||||
<span class="material-symbols-outlined text-primary mr-2">
|
||||
check_circle
|
||||
</span>
|
||||
<p class="px-2 py-1 border border-neutral-500 w-full whitespace-nowrap break-keep text-ellipsis overflow-hidden">{{ act }}</p>
|
||||
<p
|
||||
class="px-2 py-1 border border-neutral-500 w-full whitespace-nowrap break-keep text-ellipsis overflow-hidden"
|
||||
>
|
||||
{{ act }}
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
@@ -20,20 +29,26 @@
|
||||
* matched activities.
|
||||
*/
|
||||
|
||||
import { ref, watch } from 'vue';
|
||||
import emitter from '@/utils/emitter';
|
||||
import { ref, watch } from "vue";
|
||||
import emitter from "@/utils/emitter";
|
||||
|
||||
const props = defineProps(['data', 'select']);
|
||||
const props = defineProps(["data", "select"]);
|
||||
|
||||
const datadata = ref(props.select);
|
||||
|
||||
watch(() => props.data, (newValue) => {
|
||||
datadata.value = newValue;
|
||||
});
|
||||
watch(
|
||||
() => props.data,
|
||||
(newValue) => {
|
||||
datadata.value = newValue;
|
||||
},
|
||||
);
|
||||
|
||||
watch(() => props.select, (newValue) => {
|
||||
datadata.value = newValue;
|
||||
});
|
||||
watch(
|
||||
() => props.select,
|
||||
(newValue) => {
|
||||
datadata.value = newValue;
|
||||
},
|
||||
);
|
||||
|
||||
emitter.on('reset', (val) => datadata.value = val);
|
||||
emitter.on("reset", (val) => (datadata.value = val));
|
||||
</script>
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
<template>
|
||||
<ul id="cyp-conformance-result-dot">
|
||||
<li class="flex justify-start items-center py-1 pr-4" v-for="(act, index) in data" :key="index + act" :title="act">
|
||||
<span class="material-symbols-outlined disc !text-sm align-middle mr-1">fiber_manual_record</span>
|
||||
<li
|
||||
class="flex justify-start items-center py-1 pr-4"
|
||||
v-for="(act, index) in data"
|
||||
:key="index + act"
|
||||
:title="act"
|
||||
>
|
||||
<span class="material-symbols-outlined disc !text-sm align-middle mr-1"
|
||||
>fiber_manual_record</span
|
||||
>
|
||||
<span class="mr-2 block w-12">{{ act.category }}</span>
|
||||
<span class="px-2 py-1 border border-neutral-500 w-full whitespace-nowrap break-keep text-ellipsis overflow-hidden block">{{ act.task }}</span>
|
||||
<span
|
||||
class="px-2 py-1 border border-neutral-500 w-full whitespace-nowrap break-keep text-ellipsis overflow-hidden block"
|
||||
>{{ act.task }}</span
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
@@ -19,16 +29,20 @@
|
||||
* and task pairs.
|
||||
*/
|
||||
|
||||
import { ref, watch } from 'vue';
|
||||
import emitter from '@/utils/emitter';
|
||||
import { ref, watch } from "vue";
|
||||
import emitter from "@/utils/emitter";
|
||||
|
||||
const props = defineProps(['timeResultData', 'select']);
|
||||
const props = defineProps(["timeResultData", "select"]);
|
||||
|
||||
const data = ref(props.select);
|
||||
|
||||
watch(() => props.timeResultData, (newValue) => {
|
||||
data.value = newValue;
|
||||
}, { deep: true });
|
||||
watch(
|
||||
() => props.timeResultData,
|
||||
(newValue) => {
|
||||
data.value = newValue;
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
emitter.on('reset', (val) => data.value = val);
|
||||
emitter.on("reset", (val) => (data.value = val));
|
||||
</script>
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
<template>
|
||||
<div id="timeranges_s_e_container" class="flex justify-between items-center">
|
||||
<Durationjs :max="minVuemax" :min="minVuemin" :size="'min'" :updateMax="updateMax"
|
||||
@total-seconds="minTotalSeconds" :value="durationMin">
|
||||
<Durationjs
|
||||
:max="minVuemax"
|
||||
:min="minVuemin"
|
||||
:size="'min'"
|
||||
:updateMax="updateMax"
|
||||
@total-seconds="minTotalSeconds"
|
||||
:value="durationMin"
|
||||
>
|
||||
</Durationjs>
|
||||
<span>~</span>
|
||||
<Durationjs :max="maxVuemax" :min="maxVuemin" :size="'max'" :updateMin="updateMin"
|
||||
@total-seconds="maxTotalSeconds" :value="durationMax">
|
||||
<span>~</span>
|
||||
<Durationjs
|
||||
:max="maxVuemax"
|
||||
:min="maxVuemin"
|
||||
:size="'max'"
|
||||
:updateMin="updateMin"
|
||||
@total-seconds="maxTotalSeconds"
|
||||
:value="durationMax"
|
||||
>
|
||||
</Durationjs>
|
||||
</div>
|
||||
</template>
|
||||
@@ -22,11 +34,11 @@
|
||||
* for conformance time-based rules.
|
||||
*/
|
||||
|
||||
import { ref, watch } from 'vue';
|
||||
import Durationjs from '@/components/durationjs.vue';
|
||||
import { ref, watch } from "vue";
|
||||
import Durationjs from "@/components/durationjs.vue";
|
||||
|
||||
const props = defineProps(['time', 'select']);
|
||||
const emit = defineEmits(['min-total-seconds', 'max-total-seconds']);
|
||||
const props = defineProps(["time", "select"]);
|
||||
const emit = defineEmits(["min-total-seconds", "max-total-seconds"]);
|
||||
|
||||
const timeData = ref({ min: 0, max: 0 });
|
||||
const timeRangeMin = ref(0);
|
||||
@@ -56,7 +68,7 @@ function setTimeValue() {
|
||||
function minTotalSeconds(e) {
|
||||
timeRangeMin.value = e;
|
||||
updateMin.value = e;
|
||||
emit('min-total-seconds', e);
|
||||
emit("min-total-seconds", e);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,29 +78,33 @@ function minTotalSeconds(e) {
|
||||
function maxTotalSeconds(e) {
|
||||
timeRangeMax.value = e;
|
||||
updateMax.value = e;
|
||||
emit('max-total-seconds', e);
|
||||
emit("max-total-seconds", e);
|
||||
}
|
||||
|
||||
watch(() => props.time, (newValue, oldValue) => {
|
||||
durationMax.value = null;
|
||||
durationMin.value = null;
|
||||
if(newValue === null) {
|
||||
timeData.value = { min: 0, max: 0 };
|
||||
}else if(newValue !== null) {
|
||||
timeData.value = { min: newValue.min, max: newValue.max };
|
||||
emit('min-total-seconds', newValue.min);
|
||||
emit('max-total-seconds', newValue.max);
|
||||
}
|
||||
setTimeValue();
|
||||
}, { deep: true, immediate: true });
|
||||
watch(
|
||||
() => props.time,
|
||||
(newValue, oldValue) => {
|
||||
durationMax.value = null;
|
||||
durationMin.value = null;
|
||||
if (newValue === null) {
|
||||
timeData.value = { min: 0, max: 0 };
|
||||
} else if (newValue !== null) {
|
||||
timeData.value = { min: newValue.min, max: newValue.max };
|
||||
emit("min-total-seconds", newValue.min);
|
||||
emit("max-total-seconds", newValue.max);
|
||||
}
|
||||
setTimeValue();
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
);
|
||||
|
||||
// created
|
||||
if(props.select){
|
||||
if(Object.keys(props.select.base).length !== 0) {
|
||||
if (props.select) {
|
||||
if (Object.keys(props.select.base).length !== 0) {
|
||||
timeData.value = props.select.base;
|
||||
setTimeValue();
|
||||
}
|
||||
if(Object.keys(props.select.rule).length !== 0) {
|
||||
if (Object.keys(props.select.rule).length !== 0) {
|
||||
durationMin.value = props.select.rule.min;
|
||||
durationMax.value = props.select.rule.max;
|
||||
}
|
||||
|
||||
@@ -1,65 +1,112 @@
|
||||
<template>
|
||||
<Dialog :visible="listModal" @update:visible="emit('closeModal', $event)" modal :style="{ width: '90vw', height: '90vh' }" :contentClass="contentClass">
|
||||
<template #header>
|
||||
<div class=" py-5">
|
||||
<p class="text-base font-bold">Non-conformance Issue</p>
|
||||
</div>
|
||||
</template>
|
||||
<div class="h-full flex items-start justify-start p-4">
|
||||
<!-- Trace List -->
|
||||
<section class="w-80 h-full pr-4">
|
||||
<p class="h2 px-2 mb-2">Trace List ({{ traceTotal }})</p>
|
||||
<p class="text-primary h2 px-2 mb-2">
|
||||
<span class="material-symbols-outlined !text-sm align-[-10%] mr-2">info</span>Click trace number to see more.
|
||||
</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>
|
||||
<th class="h2 px-2 border-b border-neutral-500 text-start" colspan="3">Occurrences</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(trace, key) in traceList" :key="key" class=" cursor-pointer hover:text-primary" @click="switchCaseData(trace.id)">
|
||||
<td class="p-2">#{{ trace.id }}</td>
|
||||
<td class="p-2 w-24">
|
||||
<div class="h-4 w-full bg-neutral-300 rounded-sm overflow-hidden">
|
||||
<div class="h-full bg-primary" :style="progressWidth(trace.value)"></div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="py-2 text-right">{{ trace.count }}</td>
|
||||
<td class="p-2 text-right">{{ trace.ratio }}%</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<Dialog
|
||||
:visible="listModal"
|
||||
@update:visible="emit('closeModal', $event)"
|
||||
modal
|
||||
:style="{ width: '90vw', height: '90vh' }"
|
||||
:contentClass="contentClass"
|
||||
>
|
||||
<template #header>
|
||||
<div class="py-5">
|
||||
<p class="text-base font-bold">Non-conformance Issue</p>
|
||||
</div>
|
||||
</section>
|
||||
<!-- Trace item Table -->
|
||||
<section class="px-4 py-2 h-full w-[calc(100%_-_320px)] bg-neutral-10 rounded-xl">
|
||||
<p class="h2 mb-2 px-4">Trace #{{ showTraceId }}</p>
|
||||
<div class="h-36 w-full px-2 mb-2 border border-neutral-300 rounded">
|
||||
<div class="h-full w-full">
|
||||
<div id="cfmTrace" ref="cfmTrace" class="h-full min-w-full relative"></div>
|
||||
</template>
|
||||
<div class="h-full flex items-start justify-start p-4">
|
||||
<!-- Trace List -->
|
||||
<section class="w-80 h-full pr-4">
|
||||
<p class="h2 px-2 mb-2">Trace List ({{ traceTotal }})</p>
|
||||
<p class="text-primary h2 px-2 mb-2">
|
||||
<span class="material-symbols-outlined !text-sm align-[-10%] mr-2"
|
||||
>info</span
|
||||
>Click trace number to see more.
|
||||
</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>
|
||||
<th
|
||||
class="h2 px-2 border-b border-neutral-500 text-start"
|
||||
colspan="3"
|
||||
>
|
||||
Occurrences
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(trace, key) in traceList"
|
||||
:key="key"
|
||||
class="cursor-pointer hover:text-primary"
|
||||
@click="switchCaseData(trace.id)"
|
||||
>
|
||||
<td class="p-2">#{{ trace.id }}</td>
|
||||
<td class="p-2 w-24">
|
||||
<div
|
||||
class="h-4 w-full bg-neutral-300 rounded-sm overflow-hidden"
|
||||
>
|
||||
<div
|
||||
class="h-full bg-primary"
|
||||
:style="progressWidth(trace.value)"
|
||||
></div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="py-2 text-right">{{ trace.count }}</td>
|
||||
<td class="p-2 text-right">{{ trace.ratio }}%</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-y-auto overflow-x-auto scrollbar h-[calc(100%_-_200px)] infiniteTable" @scroll="handleScroll">
|
||||
<DataTable :value="caseData" showGridlines tableClass="text-sm" breakpoint="0">
|
||||
</section>
|
||||
<!-- Trace item Table -->
|
||||
<section
|
||||
class="px-4 py-2 h-full w-[calc(100%_-_320px)] bg-neutral-10 rounded-xl"
|
||||
>
|
||||
<p class="h2 mb-2 px-4">Trace #{{ showTraceId }}</p>
|
||||
<div class="h-36 w-full px-2 mb-2 border border-neutral-300 rounded">
|
||||
<div class="h-full w-full">
|
||||
<div
|
||||
id="cfmTrace"
|
||||
ref="cfmTrace"
|
||||
class="h-full min-w-full relative"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="overflow-y-auto overflow-x-auto scrollbar h-[calc(100%_-_200px)] infiniteTable"
|
||||
@scroll="handleScroll"
|
||||
>
|
||||
<DataTable
|
||||
:value="caseData"
|
||||
showGridlines
|
||||
tableClass="text-sm"
|
||||
breakpoint="0"
|
||||
>
|
||||
<div v-for="(col, index) in columnData" :key="index">
|
||||
<Column :field="col.field" :header="col.header">
|
||||
<template #body="{ data }">
|
||||
<div :class="data[col.field]?.length > 18 ? 'whitespace-normal' : 'whitespace-nowrap'">
|
||||
<div
|
||||
:class="
|
||||
data[col.field]?.length > 18
|
||||
? 'whitespace-normal'
|
||||
: 'whitespace-nowrap'
|
||||
"
|
||||
>
|
||||
{{ data[col.field] }}
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
</div>
|
||||
</DataTable>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</Dialog>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup>
|
||||
// The Lucia project.
|
||||
@@ -74,30 +121,39 @@
|
||||
* results with expandable activity sequences.
|
||||
*/
|
||||
|
||||
import { ref, computed, watch, nextTick, useTemplateRef } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useConformanceStore } from '@/stores/conformance';
|
||||
import cytoscapeMapTrace from '@/module/cytoscapeMapTrace.js';
|
||||
import { ref, computed, watch, nextTick, useTemplateRef } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useConformanceStore } from "@/stores/conformance";
|
||||
import cytoscapeMapTrace from "@/module/cytoscapeMapTrace.js";
|
||||
|
||||
const props = defineProps(['listModal', 'listNo', 'traceId', 'firstCases', 'listTraces', 'taskSeq', 'cases', 'category']);
|
||||
const emit = defineEmits(['closeModal']);
|
||||
const props = defineProps([
|
||||
"listModal",
|
||||
"listNo",
|
||||
"traceId",
|
||||
"firstCases",
|
||||
"listTraces",
|
||||
"taskSeq",
|
||||
"cases",
|
||||
"category",
|
||||
]);
|
||||
const emit = defineEmits(["closeModal"]);
|
||||
|
||||
const conformanceStore = useConformanceStore();
|
||||
const { infinite404 } = storeToRefs(conformanceStore);
|
||||
|
||||
// template ref
|
||||
const cfmTrace = useTemplateRef('cfmTrace');
|
||||
const cfmTrace = useTemplateRef("cfmTrace");
|
||||
|
||||
// data
|
||||
const contentClass = ref('!bg-neutral-100 border-t border-neutral-300 h-full');
|
||||
const contentClass = ref("!bg-neutral-100 border-t border-neutral-300 h-full");
|
||||
const showTraceId = ref(null);
|
||||
const infiniteData = ref(null);
|
||||
const maxItems = ref(false);
|
||||
const infiniteFinish = ref(true); // Whether infinite scroll loading is complete
|
||||
const startNum = ref(0);
|
||||
const processMap = ref({
|
||||
nodes:[],
|
||||
edges:[],
|
||||
nodes: [],
|
||||
edges: [],
|
||||
});
|
||||
|
||||
// computed
|
||||
@@ -106,23 +162,27 @@ const traceTotal = computed(() => {
|
||||
});
|
||||
|
||||
const traceList = computed(() => {
|
||||
const sum = props.listTraces.map(trace => trace.count).reduce((acc, cur) => acc + cur, 0);
|
||||
const sum = props.listTraces
|
||||
.map((trace) => trace.count)
|
||||
.reduce((acc, cur) => acc + cur, 0);
|
||||
|
||||
return props.listTraces.map(trace => {
|
||||
return {
|
||||
id: trace.id,
|
||||
value: Number((getPercentLabel(trace.count / sum))),
|
||||
count: trace.count.toLocaleString('en-US'),
|
||||
count_base: trace.count,
|
||||
ratio: getPercentLabel(trace.count / sum),
|
||||
};
|
||||
}).sort((x, y) => x.id - y.id);
|
||||
return props.listTraces
|
||||
.map((trace) => {
|
||||
return {
|
||||
id: trace.id,
|
||||
value: Number(getPercentLabel(trace.count / sum)),
|
||||
count: trace.count.toLocaleString("en-US"),
|
||||
count_base: trace.count,
|
||||
ratio: getPercentLabel(trace.count / sum),
|
||||
};
|
||||
})
|
||||
.sort((x, y) => x.id - y.id);
|
||||
});
|
||||
|
||||
const caseData = computed(() => {
|
||||
if(infiniteData.value !== null){
|
||||
if (infiniteData.value !== null) {
|
||||
const data = JSON.parse(JSON.stringify(infiniteData.value)); // Deep copy the original cases data
|
||||
data.forEach(item => {
|
||||
data.forEach((item) => {
|
||||
item.facets.forEach((facet, index) => {
|
||||
item[`fac_${index}`] = facet.value; // Create a new key-value pair
|
||||
});
|
||||
@@ -132,47 +192,74 @@ const caseData = computed(() => {
|
||||
item[`att_${index}`] = attribute.value; // Create a new key-value pair
|
||||
});
|
||||
delete item.attributes; // Remove the original attributes property
|
||||
})
|
||||
});
|
||||
return data;
|
||||
}
|
||||
});
|
||||
|
||||
const columnData = computed(() => {
|
||||
const data = JSON.parse(JSON.stringify(props.cases)); // Deep copy the original cases data
|
||||
const facetName = facName => facName.trim().replace(/^(.)(.*)$/, (match, firstChar, restOfString) => firstChar.toUpperCase() + restOfString.toLowerCase());
|
||||
const facetName = (facName) =>
|
||||
facName
|
||||
.trim()
|
||||
.replace(
|
||||
/^(.)(.*)$/,
|
||||
(match, firstChar, restOfString) =>
|
||||
firstChar.toUpperCase() + restOfString.toLowerCase(),
|
||||
);
|
||||
|
||||
const result = [
|
||||
{ field: 'id', header: 'Case Id' },
|
||||
{ field: 'started_at', header: 'Start time' },
|
||||
{ field: 'completed_at', header: 'End time' },
|
||||
...data[0].facets.map((fac, index) => ({ field: `fac_${index}`, header: facetName(fac.name) })),
|
||||
...data[0].attributes.map((att, index) => ({ field: `att_${index}`, header: att.key })),
|
||||
{ field: "id", header: "Case Id" },
|
||||
{ field: "started_at", header: "Start time" },
|
||||
{ field: "completed_at", header: "End time" },
|
||||
...data[0].facets.map((fac, index) => ({
|
||||
field: `fac_${index}`,
|
||||
header: facetName(fac.name),
|
||||
})),
|
||||
...data[0].attributes.map((att, index) => ({
|
||||
field: `att_${index}`,
|
||||
header: att.key,
|
||||
})),
|
||||
];
|
||||
return result
|
||||
return result;
|
||||
});
|
||||
|
||||
// watch
|
||||
watch(() => props.listModal, (newValue) => { // Draw the chart when the modal is opened for the first time
|
||||
if(newValue) createCy();
|
||||
});
|
||||
watch(
|
||||
() => props.listModal,
|
||||
(newValue) => {
|
||||
// Draw the chart when the modal is opened for the first time
|
||||
if (newValue) createCy();
|
||||
},
|
||||
);
|
||||
|
||||
watch(() => props.taskSeq, (newValue) => {
|
||||
if (newValue !== null) createCy();
|
||||
});
|
||||
watch(
|
||||
() => props.taskSeq,
|
||||
(newValue) => {
|
||||
if (newValue !== null) createCy();
|
||||
},
|
||||
);
|
||||
|
||||
watch(() => props.traceId, (newValue) => {
|
||||
// Update showTraceId when the traceId prop changes
|
||||
showTraceId.value = newValue;
|
||||
});
|
||||
watch(
|
||||
() => props.traceId,
|
||||
(newValue) => {
|
||||
// Update showTraceId when the traceId prop changes
|
||||
showTraceId.value = newValue;
|
||||
},
|
||||
);
|
||||
|
||||
watch(showTraceId, (newValue, oldValue) => {
|
||||
const isScrollTop = document.querySelector('.infiniteTable');
|
||||
if(isScrollTop && typeof isScrollTop.scrollTop !== 'undefined') if(newValue !== oldValue) isScrollTop.scrollTop = 0;
|
||||
const isScrollTop = document.querySelector(".infiniteTable");
|
||||
if (isScrollTop && typeof isScrollTop.scrollTop !== "undefined")
|
||||
if (newValue !== oldValue) isScrollTop.scrollTop = 0;
|
||||
});
|
||||
|
||||
watch(() => props.firstCases, (newValue) => {
|
||||
infiniteData.value = newValue;
|
||||
});
|
||||
watch(
|
||||
() => props.firstCases,
|
||||
(newValue) => {
|
||||
infiniteData.value = newValue;
|
||||
},
|
||||
);
|
||||
|
||||
watch(infinite404, (newValue) => {
|
||||
if (newValue === 404) maxItems.value = true;
|
||||
@@ -184,8 +271,8 @@ watch(infinite404, (newValue) => {
|
||||
* @param {number} val - The raw ratio value.
|
||||
* @returns {string} The formatted percentage string.
|
||||
*/
|
||||
function getPercentLabel(val){
|
||||
if((val * 100).toFixed(1) >= 100) return 100;
|
||||
function getPercentLabel(val) {
|
||||
if ((val * 100).toFixed(1) >= 100) return 100;
|
||||
else return parseFloat((val * 100).toFixed(1));
|
||||
}
|
||||
/**
|
||||
@@ -193,78 +280,93 @@ function getPercentLabel(val){
|
||||
* @param {number} value - The percentage value.
|
||||
* @returns {string} The CSS width style string.
|
||||
*/
|
||||
function progressWidth(value){
|
||||
return `width:${value}%;`
|
||||
function progressWidth(value) {
|
||||
return `width:${value}%;`;
|
||||
}
|
||||
/**
|
||||
* switch case data
|
||||
* @param {number} id case id
|
||||
*/
|
||||
async function switchCaseData(id) {
|
||||
if(id == showTraceId.value) return;
|
||||
if (id == showTraceId.value) return;
|
||||
infinite404.value = null;
|
||||
maxItems.value = false;
|
||||
startNum.value = 0;
|
||||
|
||||
let result;
|
||||
if(props.category === 'issue') result = await conformanceStore.getConformanceTraceDetail(props.listNo, id, 0);
|
||||
else if(props.category === 'loop') result = await conformanceStore.getConformanceLoopsTraceDetail(props.listNo, id, 0);
|
||||
if (props.category === "issue")
|
||||
result = await conformanceStore.getConformanceTraceDetail(
|
||||
props.listNo,
|
||||
id,
|
||||
0,
|
||||
);
|
||||
else if (props.category === "loop")
|
||||
result = await conformanceStore.getConformanceLoopsTraceDetail(
|
||||
props.listNo,
|
||||
id,
|
||||
0,
|
||||
);
|
||||
infiniteData.value = await result;
|
||||
showTraceId.value = id; // Set after getDetail so the case table finishes loading before switching showTraceId
|
||||
}
|
||||
/**
|
||||
* Assembles the trace element nodes data for Cytoscape rendering.
|
||||
*/
|
||||
function setNodesData(){
|
||||
function setNodesData() {
|
||||
// Clear nodes to prevent accumulation on each render
|
||||
processMap.value.nodes = [];
|
||||
// Populate nodes with data returned from the API call
|
||||
if(props.taskSeq !== null) {
|
||||
if (props.taskSeq !== null) {
|
||||
props.taskSeq.forEach((node, index) => {
|
||||
processMap.value.nodes.push({
|
||||
data: {
|
||||
id: index,
|
||||
label: node,
|
||||
backgroundColor: '#CCE5FF',
|
||||
bordercolor: '#003366',
|
||||
shape: 'round-rectangle',
|
||||
backgroundColor: "#CCE5FF",
|
||||
bordercolor: "#003366",
|
||||
shape: "round-rectangle",
|
||||
height: 80,
|
||||
width: 100
|
||||
}
|
||||
width: 100,
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Assembles the trace edge line data for Cytoscape rendering.
|
||||
*/
|
||||
function setEdgesData(){
|
||||
function setEdgesData() {
|
||||
processMap.value.edges = [];
|
||||
if(props.taskSeq !== null) {
|
||||
if (props.taskSeq !== null) {
|
||||
props.taskSeq.forEach((edge, index) => {
|
||||
processMap.value.edges.push({
|
||||
data: {
|
||||
source: `${index}`,
|
||||
target: `${index + 1}`,
|
||||
lineWidth: 1,
|
||||
style: 'solid'
|
||||
}
|
||||
style: "solid",
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
// The number of edges is one less than the number of nodes
|
||||
processMap.value.edges.pop();
|
||||
}
|
||||
/**
|
||||
* create trace cytoscape's map
|
||||
*/
|
||||
function createCy(){
|
||||
function createCy() {
|
||||
nextTick(() => {
|
||||
const graphId = cfmTrace.value;
|
||||
|
||||
setNodesData();
|
||||
setEdgesData();
|
||||
if(graphId !== null) cytoscapeMapTrace(processMap.value.nodes, processMap.value.edges, graphId);
|
||||
if (graphId !== null)
|
||||
cytoscapeMapTrace(
|
||||
processMap.value.nodes,
|
||||
processMap.value.edges,
|
||||
graphId,
|
||||
);
|
||||
});
|
||||
}
|
||||
/**
|
||||
@@ -273,12 +375,16 @@ function createCy(){
|
||||
async function fetchData() {
|
||||
try {
|
||||
infiniteFinish.value = false;
|
||||
startNum.value += 20
|
||||
const result = await conformanceStore.getConformanceTraceDetail(props.listNo, showTraceId.value, startNum.value);
|
||||
startNum.value += 20;
|
||||
const result = await conformanceStore.getConformanceTraceDetail(
|
||||
props.listNo,
|
||||
showTraceId.value,
|
||||
startNum.value,
|
||||
);
|
||||
infiniteData.value = [...infiniteData.value, ...result];
|
||||
infiniteFinish.value = true;
|
||||
} catch(error) {
|
||||
console.error('Failed to load data:', error);
|
||||
} catch (error) {
|
||||
console.error("Failed to load data:", error);
|
||||
}
|
||||
}
|
||||
/**
|
||||
@@ -286,10 +392,16 @@ async function fetchData() {
|
||||
* @param {Event} event - The scroll event.
|
||||
*/
|
||||
function handleScroll(event) {
|
||||
if(maxItems.value || infiniteData.value.length < 20 || infiniteFinish.value === false) return;
|
||||
if (
|
||||
maxItems.value ||
|
||||
infiniteData.value.length < 20 ||
|
||||
infiniteFinish.value === false
|
||||
)
|
||||
return;
|
||||
|
||||
const container = event.target;
|
||||
const overScrollHeight = container.scrollTop + container.clientHeight + 20 >= container.scrollHeight;
|
||||
const overScrollHeight =
|
||||
container.scrollTop + container.clientHeight + 20 >= container.scrollHeight;
|
||||
|
||||
if (overScrollHeight) fetchData();
|
||||
}
|
||||
@@ -298,22 +410,22 @@ function handleScroll(event) {
|
||||
@reference "../../../assets/tailwind.css";
|
||||
/* Progress bar color */
|
||||
:deep(.p-progressbar .p-progressbar-value) {
|
||||
@apply bg-primary
|
||||
@apply bg-primary;
|
||||
}
|
||||
/* Table set */
|
||||
:deep(.p-datatable-thead) {
|
||||
@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) {
|
||||
@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
|
||||
@apply border-neutral-500 !border-t-0 text-center;
|
||||
}
|
||||
/* Center header title */
|
||||
:deep(.p-column-header-content) {
|
||||
@apply justify-center
|
||||
@apply justify-center;
|
||||
}
|
||||
:deep(.p-datatable.p-datatable-gridlines .p-datatable-tbody > tr > td) {
|
||||
min-width: 72px;
|
||||
|
||||
@@ -1,30 +1,70 @@
|
||||
<template>
|
||||
<!-- Activity List -->
|
||||
<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">
|
||||
<p class="h2">Activity List ({{ data.length }})</p>
|
||||
</div>
|
||||
<!-- Table -->
|
||||
<div class="overflow-y-auto overflow-x-auto scrollbar -mx-2 h-[calc(100%_-_64px)]">
|
||||
<table class="border-separate border-spacing-x-2 table-auto min-w-full text-sm" :class="data.length === 0? 'h-full': null">
|
||||
<caption class="hidden">Activity List</caption>
|
||||
<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>
|
||||
<th class="font-semibold leading-10 px-2 border-b border-neutral-500 text-start" colspan="3">Occurrences</th>
|
||||
<th
|
||||
class="text-start font-semibold leading-10 px-2 border-b border-neutral-500"
|
||||
>
|
||||
Activity
|
||||
</th>
|
||||
<th
|
||||
class="font-semibold leading-10 px-2 border-b border-neutral-500 text-start"
|
||||
colspan="3"
|
||||
>
|
||||
Occurrences
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<Draggable :list="data" :group="{ name: 'activity', pull: 'clone', put: false }" itemKey="name" tag="tbody" animation="300" @end="onEnd" :fallbackTolerance="5" :forceFallback="true" :ghostClass="'ghostSelected'" :dragClass="'dragSelected'" :sort="false">
|
||||
<Draggable
|
||||
:list="data"
|
||||
:group="{ name: 'activity', pull: 'clone', put: false }"
|
||||
itemKey="name"
|
||||
tag="tbody"
|
||||
animation="300"
|
||||
@end="onEnd"
|
||||
:fallbackTolerance="5"
|
||||
:forceFallback="true"
|
||||
:ghostClass="'ghostSelected'"
|
||||
:dragClass="'dragSelected'"
|
||||
:sort="false"
|
||||
>
|
||||
<template #item="{ element, index }">
|
||||
<tr @dblclick="moveActItem(index, element)" :class="listSequence.includes(element) ? 'text-primary' : ''">
|
||||
<tr
|
||||
@dblclick="moveActItem(index, element)"
|
||||
:class="listSequence.includes(element) ? 'text-primary' : ''"
|
||||
>
|
||||
<td class="px-4 py-2" :id="element.label">{{ element.label }}</td>
|
||||
<td class="px-4 py-2 w-24">
|
||||
<div class="h-4 min-w-[96px] bg-neutral-300 rounded-sm overflow-hidden">
|
||||
<div class="h-full bg-primary" :style="progressWidth(element.occ_value)"></div>
|
||||
<div
|
||||
class="h-4 min-w-[96px] bg-neutral-300 rounded-sm overflow-hidden"
|
||||
>
|
||||
<div
|
||||
class="h-full bg-primary"
|
||||
:style="progressWidth(element.occ_value)"
|
||||
></div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-4 py-2 text-right">{{ element.occurrences }}</td>
|
||||
<td class="px-4 py-2 text-right">{{ element.occurrence_ratio }}</td>
|
||||
<td class="px-4 py-2 text-right">
|
||||
{{ element.occurrence_ratio }}
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</Draggable>
|
||||
@@ -32,24 +72,61 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- Sequence -->
|
||||
<div class="bg-neutral-10 border border-neutral-300 rounded-xl px-4 pb-4 w-full h-full relative text-sm">
|
||||
<p class="h2 border-b border-500 my-2">Sequence ({{ listSeq.length }})</p>
|
||||
<div
|
||||
class="bg-neutral-10 border border-neutral-300 rounded-xl px-4 pb-4 w-full h-full relative text-sm"
|
||||
>
|
||||
<p class="h2 border-b border-500 my-2">
|
||||
Sequence ({{ listSeq.length }})
|
||||
</p>
|
||||
<!-- No Data -->
|
||||
<div v-if="listSequence.length === 0" class="p-4 w-[calc(100%_-_32px)] h-5/6 flex justify-center items-center absolute">
|
||||
<p class="text-neutral-500">Please drag and drop at least two activities here and sort.</p>
|
||||
<div
|
||||
v-if="listSequence.length === 0"
|
||||
class="p-4 w-[calc(100%_-_32px)] h-5/6 flex justify-center items-center absolute"
|
||||
>
|
||||
<p class="text-neutral-500">
|
||||
Please drag and drop at least two activities here and sort.
|
||||
</p>
|
||||
</div>
|
||||
<!-- Have Data -->
|
||||
<div class="py-4 m-auto w-full h-[calc(100%_-_56px)]">
|
||||
<div class="w-full h-full overflow-y-auto overflow-x-auto scrollbar px-4 text-center listSequence">
|
||||
<draggable class="h-full" :list="listSequence" :group="{name: 'activity'}" itemKey="name" animation="300" :forceFallback="true" :fallbackTolerance="5" :dragClass="'!opacity-100'" @start="onStart" @end="onEnd" :component-data="getComponentData()" :ghostClass="'!opacity-0'">
|
||||
<div class="py-4 m-auto w-full h-[calc(100%_-_56px)]">
|
||||
<div
|
||||
class="w-full h-full overflow-y-auto overflow-x-auto scrollbar px-4 text-center listSequence"
|
||||
>
|
||||
<draggable
|
||||
class="h-full"
|
||||
:list="listSequence"
|
||||
:group="{ name: 'activity' }"
|
||||
itemKey="name"
|
||||
animation="300"
|
||||
:forceFallback="true"
|
||||
:fallbackTolerance="5"
|
||||
:dragClass="'!opacity-100'"
|
||||
@start="onStart"
|
||||
@end="onEnd"
|
||||
:component-data="getComponentData()"
|
||||
:ghostClass="'!opacity-0'"
|
||||
>
|
||||
<template #item="{ element, index }">
|
||||
<div>
|
||||
<div class="flex justify-center items-center">
|
||||
<div class="w-full p-2 border border-primary rounded text-primary bg-neutral-10" @dblclick="moveSeqItem(index, element)"><span>{{ element.label }}</span>
|
||||
<div
|
||||
class="w-full p-2 border border-primary rounded text-primary bg-neutral-10"
|
||||
@dblclick="moveSeqItem(index, element)"
|
||||
>
|
||||
<span>{{ element.label }}</span>
|
||||
</div>
|
||||
<span class="material-symbols-outlined pl-1 cursor-pointer duration-300 hover:text-danger" @click.stop.native="moveSeqItem(index, element)">close</span>
|
||||
<span
|
||||
class="material-symbols-outlined pl-1 cursor-pointer duration-300 hover:text-danger"
|
||||
@click.stop.native="moveSeqItem(index, element)"
|
||||
>close</span
|
||||
>
|
||||
</div>
|
||||
<span v-show="index !== listSeq.length - 1 && index !== lastItemIndex - 1" class="pi pi-chevron-down !text-lg inline-block py-2 pr-7"></span>
|
||||
<span
|
||||
v-show="
|
||||
index !== listSeq.length - 1 && index !== lastItemIndex - 1
|
||||
"
|
||||
class="pi pi-chevron-down !text-lg inline-block py-2 pr-7"
|
||||
></span>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
@@ -68,8 +145,8 @@
|
||||
* rules.
|
||||
*/
|
||||
|
||||
import { ref, computed, watch } from 'vue';
|
||||
import { sortNumEngZhtwForFilter } from '@/module/sortNumEngZhtw.js';
|
||||
import { ref, computed, watch } from "vue";
|
||||
import { sortNumEngZhtwForFilter } from "@/module/sortNumEngZhtw.js";
|
||||
|
||||
const props = defineProps({
|
||||
filterTaskData: {
|
||||
@@ -83,10 +160,10 @@ const props = defineProps({
|
||||
listSeq: {
|
||||
type: Array,
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:listSeq']);
|
||||
const emit = defineEmits(["update:listSeq"]);
|
||||
|
||||
const listSequence = ref([]);
|
||||
const filteredData = ref(props.filterTaskData);
|
||||
@@ -101,13 +178,19 @@ const data = computed(() => {
|
||||
return filteredData.value;
|
||||
});
|
||||
|
||||
watch(() => props.listSeq, (newval) => {
|
||||
listSequence.value = newval;
|
||||
});
|
||||
watch(
|
||||
() => props.listSeq,
|
||||
(newval) => {
|
||||
listSequence.value = newval;
|
||||
},
|
||||
);
|
||||
|
||||
watch(() => props.filterTaskData, (newval) => {
|
||||
filteredData.value = newval;
|
||||
});
|
||||
watch(
|
||||
() => props.filterTaskData,
|
||||
(newval) => {
|
||||
filteredData.value = newval;
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Moves an activity from the list to the sequence on double-click.
|
||||
@@ -129,7 +212,7 @@ function moveSeqItem(index, element) {
|
||||
|
||||
/** Emits the current sequence list to the parent component. */
|
||||
function getComponentData() {
|
||||
emit('update:listSeq', listSequence.value);
|
||||
emit("update:listSeq", listSequence.value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,13 +221,13 @@ function getComponentData() {
|
||||
*/
|
||||
function onStart(evt) {
|
||||
const lastChild = evt.to.lastChild.lastChild;
|
||||
lastChild.style.display = 'none';
|
||||
lastChild.style.display = "none";
|
||||
// Hide the dragged element at its original position
|
||||
const originalElement = evt.item;
|
||||
originalElement.style.display = 'none';
|
||||
originalElement.style.display = "none";
|
||||
// When dragging the last element, hide the arrow of the second-to-last element
|
||||
const listIndex = listSequence.value.length - 1;
|
||||
if(evt.oldIndex === listIndex) lastItemIndex.value = listIndex;
|
||||
if (evt.oldIndex === listIndex) lastItemIndex.value = listIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,12 +237,12 @@ function onStart(evt) {
|
||||
function onEnd(evt) {
|
||||
// Show the dragged element
|
||||
const originalElement = evt.item;
|
||||
originalElement.style.display = '';
|
||||
originalElement.style.display = "";
|
||||
// Show the arrow after drag ends, except for the last element
|
||||
const lastChild = evt.item.lastChild;
|
||||
const listIndex = listSequence.value.length - 1
|
||||
const listIndex = listSequence.value.length - 1;
|
||||
if (evt.oldIndex !== listIndex) {
|
||||
lastChild.style.display = '';
|
||||
lastChild.style.display = "";
|
||||
}
|
||||
// Reset: hide the second-to-last element's arrow when dragging the last element
|
||||
lastItemIndex.value = null;
|
||||
@@ -168,9 +251,9 @@ function onEnd(evt) {
|
||||
<style scoped>
|
||||
@reference "../../../../assets/tailwind.css";
|
||||
.ghostSelected {
|
||||
@apply shadow-[0px_0px_100px_-10px_inset] shadow-neutral-200
|
||||
@apply shadow-[0px_0px_100px_-10px_inset] shadow-neutral-200;
|
||||
}
|
||||
.dragSelected {
|
||||
@apply shadow-[0px_0px_4px_2px] bg-neutral-10 shadow-neutral-300 !opacity-100
|
||||
@apply shadow-[0px_0px_4px_2px] bg-neutral-10 shadow-neutral-300 !opacity-100;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,29 +1,69 @@
|
||||
<template>
|
||||
<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">
|
||||
<p class="h2">{{ tableTitle }} ({{ tableData.length }})</p>
|
||||
</div>
|
||||
<!-- Table -->
|
||||
<div class="overflow-y-auto overflow-x-auto scrollbar -mx-2 h-[calc(100%_-_64px)]">
|
||||
<DataTable v-model:selection="select" :value="tableData" dataKey="label" breakpoint="0" tableClass="w-full !border-separate !border-spacing-x-2 !table-auto text-sm" @row-select="onRowSelect">
|
||||
<div
|
||||
class="overflow-y-auto overflow-x-auto scrollbar -mx-2 h-[calc(100%_-_64px)]"
|
||||
>
|
||||
<DataTable
|
||||
v-model:selection="select"
|
||||
:value="tableData"
|
||||
dataKey="label"
|
||||
breakpoint="0"
|
||||
tableClass="w-full !border-separate !border-spacing-x-2 !table-auto text-sm"
|
||||
@row-select="onRowSelect"
|
||||
>
|
||||
<ColumnGroup type="header">
|
||||
<Row>
|
||||
<Column selectionMode="single" headerClass="w-8 !p-2 !bg-neutral-10 !border-neutral-500 sticky top-0 left-0 z-10 bg-neutral-10"></Column>
|
||||
<Column field="label" header="Activity" headerClass="!bg-neutral-10 !border-neutral-500 !py-2 sticky top-0 left-0 z-10 bg-neutral-10" sortable />
|
||||
<Column field="occurrences_base" header="Occurrences" headerClass="!bg-neutral-10 !border-neutral-500 !py-2 sticky top-0 left-0 z-10 bg-neutral-10" sortable :colspan="3" />
|
||||
<Column
|
||||
selectionMode="single"
|
||||
headerClass="w-8 !p-2 !bg-neutral-10 !border-neutral-500 sticky top-0 left-0 z-10 bg-neutral-10"
|
||||
></Column>
|
||||
<Column
|
||||
field="label"
|
||||
header="Activity"
|
||||
headerClass="!bg-neutral-10 !border-neutral-500 !py-2 sticky top-0 left-0 z-10 bg-neutral-10"
|
||||
sortable
|
||||
/>
|
||||
<Column
|
||||
field="occurrences_base"
|
||||
header="Occurrences"
|
||||
headerClass="!bg-neutral-10 !border-neutral-500 !py-2 sticky top-0 left-0 z-10 bg-neutral-10"
|
||||
sortable
|
||||
:colspan="3"
|
||||
/>
|
||||
</Row>
|
||||
</ColumnGroup>
|
||||
<Column selectionMode="single" bodyClass="!p-2 !border-0"></Column>
|
||||
<Column field="label" header="Activity" bodyClass="break-words !py-2 !border-0"></Column>
|
||||
<Column
|
||||
field="label"
|
||||
header="Activity"
|
||||
bodyClass="break-words !py-2 !border-0"
|
||||
></Column>
|
||||
<Column header="Progress" bodyClass="!py-2 !border-0 min-w-[96px]">
|
||||
<template #body="slotProps">
|
||||
<div class="h-4 w-full bg-neutral-300 rounded-sm overflow-hidden">
|
||||
<div class="h-full bg-primary" :style="progressWidth(slotProps.data.occ_value)"></div>
|
||||
<div
|
||||
class="h-full bg-primary"
|
||||
:style="progressWidth(slotProps.data.occ_value)"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="occurrences" header="Occurrences" bodyClass="!text-right !py-2 !border-0"></Column>
|
||||
<Column field="occurrence_ratio" header="Occurrence Ratio" bodyClass="!text-right !py-2 !border-0"></Column>
|
||||
<Column
|
||||
field="occurrences"
|
||||
header="Occurrences"
|
||||
bodyClass="!text-right !py-2 !border-0"
|
||||
></Column>
|
||||
<Column
|
||||
field="occurrence_ratio"
|
||||
header="Occurrence Ratio"
|
||||
bodyClass="!text-right !py-2 !border-0"
|
||||
></Column>
|
||||
</DataTable>
|
||||
</div>
|
||||
</div>
|
||||
@@ -38,7 +78,7 @@
|
||||
* occurrences filter table with single-row radio selection.
|
||||
*/
|
||||
|
||||
import { ref, watch } from 'vue';
|
||||
import { ref, watch } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
tableTitle: {
|
||||
@@ -51,28 +91,31 @@ const props = defineProps({
|
||||
},
|
||||
tableSelect: {
|
||||
type: [Object, Array],
|
||||
default: null
|
||||
default: null,
|
||||
},
|
||||
progressWidth: {
|
||||
type: Function,
|
||||
required: false,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['on-row-select']);
|
||||
const emit = defineEmits(["on-row-select"]);
|
||||
|
||||
const select = ref(null);
|
||||
const metaKey = ref(true);
|
||||
|
||||
watch(() => props.tableSelect, (newval) => {
|
||||
select.value = newval;
|
||||
});
|
||||
watch(
|
||||
() => props.tableSelect,
|
||||
(newval) => {
|
||||
select.value = newval;
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Emits the selected row to the parent component.
|
||||
* @param {Event} e - The row selection event.
|
||||
*/
|
||||
function onRowSelect(e) {
|
||||
emit('on-row-select', e);
|
||||
emit("on-row-select", e);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -4,36 +4,93 @@
|
||||
<p class="h2">{{ tableTitle }} ({{ data.length }})</p>
|
||||
</div>
|
||||
<!-- Table -->
|
||||
<div class="overflow-y-auto overflow-x-auto scrollbar -mx-2 h-[calc(100%_-_64px)]">
|
||||
<DataTable v-model:selection="select" :value="data" breakpoint="0" tableClass="w-full !border-separate !border-spacing-x-2 !table-auto text-sm" @row-select="onRowSelect" @row-unselect="onRowUnselect" @row-select-all="onRowSelectAll" @row-unselect-all="onRowUnelectAll">
|
||||
<div
|
||||
class="overflow-y-auto overflow-x-auto scrollbar -mx-2 h-[calc(100%_-_64px)]"
|
||||
>
|
||||
<DataTable
|
||||
v-model:selection="select"
|
||||
:value="data"
|
||||
breakpoint="0"
|
||||
tableClass="w-full !border-separate !border-spacing-x-2 !table-auto text-sm"
|
||||
@row-select="onRowSelect"
|
||||
@row-unselect="onRowUnselect"
|
||||
@row-select-all="onRowSelectAll"
|
||||
@row-unselect-all="onRowUnelectAll"
|
||||
>
|
||||
<ColumnGroup type="header">
|
||||
<Row>
|
||||
<Column selectionMode="multiple" headerClass="w-8 !p-2 !bg-neutral-10 !border-neutral-500 sticky top-0 left-0 z-10 bg-neutral-10 allCheckboxAct"></Column>
|
||||
<Column field="label" header="Activity" headerClass="!bg-neutral-10 !border-neutral-500 !py-2 sticky top-0 left-0 z-10 bg-neutral-10" sortable />
|
||||
<Column field="occurrences_base" header="Occurrences" headerClass="!bg-neutral-10 !border-neutral-500 !py-2 sticky top-0 left-0 z-10 bg-neutral-10" sortable :colspan="3" />
|
||||
<Column field="cases_base" headerClass="!bg-neutral-10 !border-neutral-500 !py-2 sticky top-0 left-0 z-10 bg-neutral-10" header="Cases with Activity" sortable :colspan="3" />
|
||||
<Column
|
||||
selectionMode="multiple"
|
||||
headerClass="w-8 !p-2 !bg-neutral-10 !border-neutral-500 sticky top-0 left-0 z-10 bg-neutral-10 allCheckboxAct"
|
||||
></Column>
|
||||
<Column
|
||||
field="label"
|
||||
header="Activity"
|
||||
headerClass="!bg-neutral-10 !border-neutral-500 !py-2 sticky top-0 left-0 z-10 bg-neutral-10"
|
||||
sortable
|
||||
/>
|
||||
<Column
|
||||
field="occurrences_base"
|
||||
header="Occurrences"
|
||||
headerClass="!bg-neutral-10 !border-neutral-500 !py-2 sticky top-0 left-0 z-10 bg-neutral-10"
|
||||
sortable
|
||||
:colspan="3"
|
||||
/>
|
||||
<Column
|
||||
field="cases_base"
|
||||
headerClass="!bg-neutral-10 !border-neutral-500 !py-2 sticky top-0 left-0 z-10 bg-neutral-10"
|
||||
header="Cases with Activity"
|
||||
sortable
|
||||
:colspan="3"
|
||||
/>
|
||||
</Row>
|
||||
</ColumnGroup>
|
||||
<Column selectionMode="multiple" bodyClass="!p-2 !border-0"></Column>
|
||||
<Column field="label" header="Activity" bodyClass="break-words !py-2 !border-0"></Column>
|
||||
<Column
|
||||
field="label"
|
||||
header="Activity"
|
||||
bodyClass="break-words !py-2 !border-0"
|
||||
></Column>
|
||||
<Column header="Progress" bodyClass="!py-2 !border-0 min-w-[96px]">
|
||||
<template #body="slotProps">
|
||||
<div class="h-4 w-full bg-neutral-300 rounded-sm overflow-hidden">
|
||||
<div class="h-full bg-primary" :style="progressWidth(slotProps.data.occ_value)"></div>
|
||||
<div
|
||||
class="h-full bg-primary"
|
||||
:style="progressWidth(slotProps.data.occ_value)"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="occurrences" header="Occurrences" bodyClass="!text-right !py-2 !border-0"></Column>
|
||||
<Column field="occurrence_ratio" header="O2" bodyClass="!text-right !py-2 !border-0"></Column>
|
||||
<Column
|
||||
field="occurrences"
|
||||
header="Occurrences"
|
||||
bodyClass="!text-right !py-2 !border-0"
|
||||
></Column>
|
||||
<Column
|
||||
field="occurrence_ratio"
|
||||
header="O2"
|
||||
bodyClass="!text-right !py-2 !border-0"
|
||||
></Column>
|
||||
<Column header="Progress" bodyClass="!py-2 !border-0 min-w-[96px]">
|
||||
<template #body="slotProps">
|
||||
<div class="h-4 w-full bg-neutral-300 rounded-sm overflow-hidden">
|
||||
<div class="h-full bg-primary" :style="progressWidth(slotProps.data.case_value)"></div>
|
||||
<div
|
||||
class="h-full bg-primary"
|
||||
:style="progressWidth(slotProps.data.case_value)"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="cases" header="Cases with Activity" bodyClass="!text-right !py-2 !border-0"></Column>
|
||||
<Column field="case_ratio" header="C2" bodyClass="!text-right !py-2 !border-0"></Column>
|
||||
<Column
|
||||
field="cases"
|
||||
header="Cases with Activity"
|
||||
bodyClass="!text-right !py-2 !border-0"
|
||||
></Column>
|
||||
<Column
|
||||
field="case_ratio"
|
||||
header="C2"
|
||||
bodyClass="!text-right !py-2 !border-0"
|
||||
></Column>
|
||||
</DataTable>
|
||||
</div>
|
||||
</div>
|
||||
@@ -50,27 +107,35 @@
|
||||
* selection.
|
||||
*/
|
||||
|
||||
import { ref, watch } from 'vue';
|
||||
import { ref, watch } from "vue";
|
||||
|
||||
const props = defineProps(['tableTitle', 'tableData', 'tableSelect', 'progressWidth']);
|
||||
const props = defineProps([
|
||||
"tableTitle",
|
||||
"tableData",
|
||||
"tableSelect",
|
||||
"progressWidth",
|
||||
]);
|
||||
|
||||
const emit = defineEmits(['on-row-select']);
|
||||
const emit = defineEmits(["on-row-select"]);
|
||||
|
||||
const select = ref(null);
|
||||
const data = ref(props.tableData);
|
||||
|
||||
watch(() => props.tableSelect, (newval) => {
|
||||
select.value = newval;
|
||||
});
|
||||
watch(
|
||||
() => props.tableSelect,
|
||||
(newval) => {
|
||||
select.value = newval;
|
||||
},
|
||||
);
|
||||
|
||||
/** Emits the current selection when a row is selected. */
|
||||
function onRowSelect() {
|
||||
emit('on-row-select', select.value);
|
||||
emit("on-row-select", select.value);
|
||||
}
|
||||
|
||||
/** Emits the current selection when a row is unselected. */
|
||||
function onRowUnselect() {
|
||||
emit('on-row-select', select.value);
|
||||
emit("on-row-select", select.value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,7 +144,7 @@ function onRowUnselect() {
|
||||
*/
|
||||
function onRowSelectAll(e) {
|
||||
select.value = e.data;
|
||||
emit('on-row-select', select.value);
|
||||
emit("on-row-select", select.value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,6 +153,6 @@ function onRowSelectAll(e) {
|
||||
*/
|
||||
function onRowUnelectAll(e) {
|
||||
select.value = null;
|
||||
emit('on-row-select', select.value);
|
||||
emit("on-row-select", select.value);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,123 +1,310 @@
|
||||
<template>
|
||||
<section class="w-full h-full">
|
||||
<p class="h2 ml-1 mb-2">Activity Select</p>
|
||||
<div class="flex flex-row justify-between items-start gap-4 w-full h-[calc(100%_-_48px)]">
|
||||
<!-- Attribute Name -->
|
||||
<div class="basis-1/3 bg-neutral-10 border border-neutral-300 rounded-xl px-4 pb-4 w-full h-full relative text-sm">
|
||||
<p class="h2 my-2">Attribute Name ({{ attTotal }})<span class="material-symbols-outlined !text-sm align-middle ml-2 cursor-pointer" v-tooltip.bottom="tooltip.attributeName">info</span></p>
|
||||
<div class="overflow-y-auto overflow-x-auto scrollbar -mx-2 h-[calc(100%_-_56px)]">
|
||||
<DataTable v-model:selection="selectedAttName" :value="filterAttrs" dataKey="key" breakpoint="0" :tableClass="tableClass" @row-select="switchAttNameRadio">
|
||||
<Column selectionMode="single" :headerClass="headerModeClass" :bodyClass="bodyModeClass"></Column>
|
||||
<Column field="key" header="Attribute" :headerClass="headerClass" :bodyClass="bodyClass" sortable>
|
||||
<template #body="slotProps">
|
||||
<div :title="slotProps.data.key" class="whitespace-nowrap break-keep overflow-hidden text-ellipsis w-full">
|
||||
<section class="w-full h-full">
|
||||
<p class="h2 ml-1 mb-2">Activity Select</p>
|
||||
<div
|
||||
class="flex flex-row justify-between items-start gap-4 w-full h-[calc(100%_-_48px)]"
|
||||
>
|
||||
<!-- Attribute Name -->
|
||||
<div
|
||||
class="basis-1/3 bg-neutral-10 border border-neutral-300 rounded-xl px-4 pb-4 w-full h-full relative text-sm"
|
||||
>
|
||||
<p class="h2 my-2">
|
||||
Attribute Name ({{ attTotal }})<span
|
||||
class="material-symbols-outlined !text-sm align-middle ml-2 cursor-pointer"
|
||||
v-tooltip.bottom="tooltip.attributeName"
|
||||
>info</span
|
||||
>
|
||||
</p>
|
||||
<div
|
||||
class="overflow-y-auto overflow-x-auto scrollbar -mx-2 h-[calc(100%_-_56px)]"
|
||||
>
|
||||
<DataTable
|
||||
v-model:selection="selectedAttName"
|
||||
:value="filterAttrs"
|
||||
dataKey="key"
|
||||
breakpoint="0"
|
||||
:tableClass="tableClass"
|
||||
@row-select="switchAttNameRadio"
|
||||
>
|
||||
<Column
|
||||
selectionMode="single"
|
||||
:headerClass="headerModeClass"
|
||||
:bodyClass="bodyModeClass"
|
||||
></Column>
|
||||
<Column
|
||||
field="key"
|
||||
header="Attribute"
|
||||
:headerClass="headerClass"
|
||||
:bodyClass="bodyClass"
|
||||
sortable
|
||||
>
|
||||
<template #body="slotProps">
|
||||
<div
|
||||
:title="slotProps.data.key"
|
||||
class="whitespace-nowrap break-keep overflow-hidden text-ellipsis w-full"
|
||||
>
|
||||
{{ slotProps.data.key }}
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
</DataTable>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Range Selection -->
|
||||
<div class="basis-2/3 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">Range Selection {{ attRangeTotal }}</p>
|
||||
</div>
|
||||
<div class="overflow-y-auto overflow-x-auto scrollbar -mx-2 w-full h-[calc(100%_-_70px)]">
|
||||
<!-- type: boolean -->
|
||||
<div v-if="selectedAttName.type === 'boolean'" class="w-full">
|
||||
<DataTable v-model:selection="selectedAttRange" :value="attRangeData" dataKey="id" breakpoint="0" :tableClass="tableClass" @row-select="onRowSelect" >
|
||||
<ColumnGroup type="header">
|
||||
<Row>
|
||||
<Column selectionMode="single" :headerClass="headerModeClass" ></Column>
|
||||
<Column field="value" header="Value" :headerClass="headerClass" sortable />
|
||||
<Column field="freq" header="Occurrences" :headerClass="headerClass" sortable :colspan="3" />
|
||||
</Row>
|
||||
</ColumnGroup>
|
||||
<Column selectionMode="single" :bodyClass="bodyModeClass"></Column>
|
||||
<Column field="label" header="Activity" :bodyClass="bodyClass">
|
||||
<template #body="slotProps">
|
||||
<div :title="slotProps.data.label" class="whitespace-nowrap break-keep overflow-hidden text-ellipsis w-full">
|
||||
{{ slotProps.data.label }}
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
<Column header="Progress" bodyClass="!py-2 !border-0 min-w-[96px]">
|
||||
<template #body="slotProps">
|
||||
<div class="h-4 w-full bg-neutral-300 rounded-sm overflow-hidden">
|
||||
<div class="h-full bg-primary" :style="progressWidth(slotProps.data.occ_progress_bar)"></div>
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="occ_value" header="Occurrences" bodyClass="!text-right !py-2 !border-0"></Column>
|
||||
<Column field="occ_ratio" header="Occurrence Ratio" bodyClass="!text-right !py-2 !border-0"></Column>
|
||||
</DataTable>
|
||||
</div>
|
||||
<!-- type: string -->
|
||||
<div v-else-if="selectedAttName.type === 'string'" class="w-full">
|
||||
<DataTable v-model:selection="selectedAttRange" :value="attRangeData" dataKey="id" breakpoint="0" tableClass="w-full !border-separate !border-spacing-x-2 !table-auto text-sm" @row-select="onRowSelect" @row-unselect="onRowUnselect" @row-select-all="onRowSelectAll($event)" @row-unselect-all="onRowUnelectAll">
|
||||
<ColumnGroup type="header">
|
||||
<Row>
|
||||
<Column selectionMode="multiple" :headerClass="headerModeClass" ></Column>
|
||||
<Column field="value" header="Value" :headerClass="headerClass" sortable />
|
||||
<Column field="freq" header="Occurrences" :headerClass="headerClass" sortable :colspan="3" />
|
||||
</Row>
|
||||
</ColumnGroup>
|
||||
<Column selectionMode="multiple" :bodyClass="bodyModeClass"></Column>
|
||||
<Column field="value" header="Activity" :bodyClass="bodyClass">
|
||||
<template #body="slotProps">
|
||||
<div :title="slotProps.data.value" class="whitespace-nowrap break-keep overflow-hidden text-ellipsis w-full">
|
||||
{{ slotProps.data.value }}
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
<Column header="Progress" bodyClass="!py-2 !border-0 min-w-[96px]">
|
||||
<template #body="slotProps">
|
||||
<div class="h-4 w-full bg-neutral-300 rounded-sm overflow-hidden">
|
||||
<div class="h-full bg-primary" :style="progressWidth(slotProps.data.occ_progress_bar)"></div>
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="occ_value" header="Occurrences" bodyClass="!text-right !py-2 !border-0"></Column>
|
||||
<Column field="occ_ratio" header="Occurrence Ratio" bodyClass="!text-right !py-2 !border-0"></Column>
|
||||
</DataTable>
|
||||
</div>
|
||||
<!-- Range Selection -->
|
||||
<div
|
||||
class="basis-2/3 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">Range Selection {{ attRangeTotal }}</p>
|
||||
</div>
|
||||
<!-- type: value -->
|
||||
<div v-else-if="valueTypes.includes(selectedAttName.type)" class="space-y-2 text-sm w-full">
|
||||
<!-- Chart.js -->
|
||||
<div class="h-3/5 relative">
|
||||
<Chart type="line" :data="chartData" :options="chartOptions" class="h-30rem" id="chartCanvasId"/>
|
||||
<div id="chart-mask-left" class="absolute bg-neutral-10/50"></div>
|
||||
<div id="chart-mask-right" class="absolute bg-neutral-10/50"></div>
|
||||
<div
|
||||
class="overflow-y-auto overflow-x-auto scrollbar -mx-2 w-full h-[calc(100%_-_70px)]"
|
||||
>
|
||||
<!-- type: boolean -->
|
||||
<div v-if="selectedAttName.type === 'boolean'" class="w-full">
|
||||
<DataTable
|
||||
v-model:selection="selectedAttRange"
|
||||
:value="attRangeData"
|
||||
dataKey="id"
|
||||
breakpoint="0"
|
||||
:tableClass="tableClass"
|
||||
@row-select="onRowSelect"
|
||||
>
|
||||
<ColumnGroup type="header">
|
||||
<Row>
|
||||
<Column
|
||||
selectionMode="single"
|
||||
:headerClass="headerModeClass"
|
||||
></Column>
|
||||
<Column
|
||||
field="value"
|
||||
header="Value"
|
||||
:headerClass="headerClass"
|
||||
sortable
|
||||
/>
|
||||
<Column
|
||||
field="freq"
|
||||
header="Occurrences"
|
||||
:headerClass="headerClass"
|
||||
sortable
|
||||
:colspan="3"
|
||||
/>
|
||||
</Row>
|
||||
</ColumnGroup>
|
||||
<Column
|
||||
selectionMode="single"
|
||||
:bodyClass="bodyModeClass"
|
||||
></Column>
|
||||
<Column field="label" header="Activity" :bodyClass="bodyClass">
|
||||
<template #body="slotProps">
|
||||
<div
|
||||
:title="slotProps.data.label"
|
||||
class="whitespace-nowrap break-keep overflow-hidden text-ellipsis w-full"
|
||||
>
|
||||
{{ slotProps.data.label }}
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
<Column
|
||||
header="Progress"
|
||||
bodyClass="!py-2 !border-0 min-w-[96px]"
|
||||
>
|
||||
<template #body="slotProps">
|
||||
<div
|
||||
class="h-4 w-full bg-neutral-300 rounded-sm overflow-hidden"
|
||||
>
|
||||
<div
|
||||
class="h-full bg-primary"
|
||||
:style="progressWidth(slotProps.data.occ_progress_bar)"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
<Column
|
||||
field="occ_value"
|
||||
header="Occurrences"
|
||||
bodyClass="!text-right !py-2 !border-0"
|
||||
></Column>
|
||||
<Column
|
||||
field="occ_ratio"
|
||||
header="Occurrence Ratio"
|
||||
bodyClass="!text-right !py-2 !border-0"
|
||||
></Column>
|
||||
</DataTable>
|
||||
</div>
|
||||
<!-- Slider -->
|
||||
<div class="px-2 py-3">
|
||||
<Slider v-model="selectArea" :step="1" :min="0" :max="selectRange" range class="mx-2" @change="changeSelectArea($event)"/>
|
||||
<!-- type: string -->
|
||||
<div v-else-if="selectedAttName.type === 'string'" class="w-full">
|
||||
<DataTable
|
||||
v-model:selection="selectedAttRange"
|
||||
:value="attRangeData"
|
||||
dataKey="id"
|
||||
breakpoint="0"
|
||||
tableClass="w-full !border-separate !border-spacing-x-2 !table-auto text-sm"
|
||||
@row-select="onRowSelect"
|
||||
@row-unselect="onRowUnselect"
|
||||
@row-select-all="onRowSelectAll($event)"
|
||||
@row-unselect-all="onRowUnelectAll"
|
||||
>
|
||||
<ColumnGroup type="header">
|
||||
<Row>
|
||||
<Column
|
||||
selectionMode="multiple"
|
||||
:headerClass="headerModeClass"
|
||||
></Column>
|
||||
<Column
|
||||
field="value"
|
||||
header="Value"
|
||||
:headerClass="headerClass"
|
||||
sortable
|
||||
/>
|
||||
<Column
|
||||
field="freq"
|
||||
header="Occurrences"
|
||||
:headerClass="headerClass"
|
||||
sortable
|
||||
:colspan="3"
|
||||
/>
|
||||
</Row>
|
||||
</ColumnGroup>
|
||||
<Column
|
||||
selectionMode="multiple"
|
||||
:bodyClass="bodyModeClass"
|
||||
></Column>
|
||||
<Column field="value" header="Activity" :bodyClass="bodyClass">
|
||||
<template #body="slotProps">
|
||||
<div
|
||||
:title="slotProps.data.value"
|
||||
class="whitespace-nowrap break-keep overflow-hidden text-ellipsis w-full"
|
||||
>
|
||||
{{ slotProps.data.value }}
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
<Column
|
||||
header="Progress"
|
||||
bodyClass="!py-2 !border-0 min-w-[96px]"
|
||||
>
|
||||
<template #body="slotProps">
|
||||
<div
|
||||
class="h-4 w-full bg-neutral-300 rounded-sm overflow-hidden"
|
||||
>
|
||||
<div
|
||||
class="h-full bg-primary"
|
||||
:style="progressWidth(slotProps.data.occ_progress_bar)"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
<Column
|
||||
field="occ_value"
|
||||
header="Occurrences"
|
||||
bodyClass="!text-right !py-2 !border-0"
|
||||
></Column>
|
||||
<Column
|
||||
field="occ_ratio"
|
||||
header="Occurrence Ratio"
|
||||
bodyClass="!text-right !py-2 !border-0"
|
||||
></Column>
|
||||
</DataTable>
|
||||
</div>
|
||||
<!-- Calendar / InputNumber group -->
|
||||
<div>
|
||||
<div v-if="selectedAttName.type === 'date'" class="flex justify-center items-center space-x-2 w-full">
|
||||
<div>
|
||||
<span class="block mb-2">Start time</span>
|
||||
<Calendar v-model="startTime" dateFormat="yy/mm/dd" :panelProps="panelProps" :minDate="startMinDate" :maxDate="startMaxDate" showTime showIcon hourFormat="24" @date-select="sliderValueRange($event, 'start')" id="startCalendar" />
|
||||
</div>
|
||||
<span class="block mt-4">~</span>
|
||||
<div>
|
||||
<span class="block mb-2">End time</span>
|
||||
<Calendar v-model="endTime" dateFormat="yy/mm/dd" :panelProps="panelProps" :minDate="endMinDate" :maxDate="endMaxDate" showTime showIcon hourFormat="24" @date-select="sliderValueRange($event, 'end')" id="endCalendar"/>
|
||||
</div>
|
||||
<!-- type: value -->
|
||||
<div
|
||||
v-else-if="valueTypes.includes(selectedAttName.type)"
|
||||
class="space-y-2 text-sm w-full"
|
||||
>
|
||||
<!-- Chart.js -->
|
||||
<div class="h-3/5 relative">
|
||||
<Chart
|
||||
type="line"
|
||||
:data="chartData"
|
||||
:options="chartOptions"
|
||||
class="h-30rem"
|
||||
id="chartCanvasId"
|
||||
/>
|
||||
<div id="chart-mask-left" class="absolute bg-neutral-10/50"></div>
|
||||
<div
|
||||
id="chart-mask-right"
|
||||
class="absolute bg-neutral-10/50"
|
||||
></div>
|
||||
</div>
|
||||
<div v-else class="flex justify-center items-center space-x-2 w-full">
|
||||
<InputNumber v-model="valueStart" :min="valueStartMin" :max="valueStartMax" :maxFractionDigits="2" inputClass="w-24 text-sm text-right" @blur="sliderValueRange($event, 'start')"></InputNumber>
|
||||
<span class="block px-2">~</span>
|
||||
<InputNumber v-model="valueEnd" :min="valueEndMin" :max="valueEndMax" inputClass="w-24 text-sm text-right" :maxFractionDigits="2" @blur="sliderValueRange($event, 'end')"></InputNumber>
|
||||
<!-- Slider -->
|
||||
<div class="px-2 py-3">
|
||||
<Slider
|
||||
v-model="selectArea"
|
||||
:step="1"
|
||||
:min="0"
|
||||
:max="selectRange"
|
||||
range
|
||||
class="mx-2"
|
||||
@change="changeSelectArea($event)"
|
||||
/>
|
||||
</div>
|
||||
<!-- Calendar / InputNumber group -->
|
||||
<div>
|
||||
<div
|
||||
v-if="selectedAttName.type === 'date'"
|
||||
class="flex justify-center items-center space-x-2 w-full"
|
||||
>
|
||||
<div>
|
||||
<span class="block mb-2">Start time</span>
|
||||
<Calendar
|
||||
v-model="startTime"
|
||||
dateFormat="yy/mm/dd"
|
||||
:panelProps="panelProps"
|
||||
:minDate="startMinDate"
|
||||
:maxDate="startMaxDate"
|
||||
showTime
|
||||
showIcon
|
||||
hourFormat="24"
|
||||
@date-select="sliderValueRange($event, 'start')"
|
||||
id="startCalendar"
|
||||
/>
|
||||
</div>
|
||||
<span class="block mt-4">~</span>
|
||||
<div>
|
||||
<span class="block mb-2">End time</span>
|
||||
<Calendar
|
||||
v-model="endTime"
|
||||
dateFormat="yy/mm/dd"
|
||||
:panelProps="panelProps"
|
||||
:minDate="endMinDate"
|
||||
:maxDate="endMaxDate"
|
||||
showTime
|
||||
showIcon
|
||||
hourFormat="24"
|
||||
@date-select="sliderValueRange($event, 'end')"
|
||||
id="endCalendar"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="flex justify-center items-center space-x-2 w-full"
|
||||
>
|
||||
<InputNumber
|
||||
v-model="valueStart"
|
||||
:min="valueStartMin"
|
||||
:max="valueStartMax"
|
||||
:maxFractionDigits="2"
|
||||
inputClass="w-24 text-sm text-right"
|
||||
@blur="sliderValueRange($event, 'start')"
|
||||
></InputNumber>
|
||||
<span class="block px-2">~</span>
|
||||
<InputNumber
|
||||
v-model="valueEnd"
|
||||
:min="valueEndMin"
|
||||
:max="valueEndMax"
|
||||
inputClass="w-24 text-sm text-right"
|
||||
:maxFractionDigits="2"
|
||||
@blur="sliderValueRange($event, 'end')"
|
||||
></InputNumber>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</template>
|
||||
<script setup>
|
||||
// The Lucia project.
|
||||
@@ -132,24 +319,24 @@
|
||||
* for filtering by attribute values.
|
||||
*/
|
||||
|
||||
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useAllMapDataStore } from '@/stores/allMapData';
|
||||
import { setLineChartData } from '@/module/setChartData.js';
|
||||
import getMoment from 'moment';
|
||||
import InputNumber from 'primevue/inputnumber';
|
||||
import { Decimal } from 'decimal.js';
|
||||
import emitter from '@/utils/emitter';
|
||||
import { ref, computed, onMounted, onBeforeUnmount } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useAllMapDataStore } from "@/stores/allMapData";
|
||||
import { setLineChartData } from "@/module/setChartData.js";
|
||||
import getMoment from "moment";
|
||||
import InputNumber from "primevue/inputnumber";
|
||||
import { Decimal } from "decimal.js";
|
||||
import emitter from "@/utils/emitter";
|
||||
|
||||
const emit = defineEmits(['select-attribute']);
|
||||
const emit = defineEmits(["select-attribute"]);
|
||||
|
||||
const allMapDataStore = useAllMapDataStore();
|
||||
const { filterAttrs } = storeToRefs(allMapDataStore);
|
||||
|
||||
const selectedAttName = ref({});
|
||||
const selectedAttRange = ref(null);
|
||||
const valueTypes = ['int', 'float', 'date'];
|
||||
const classTypes = ['boolean', 'string'];
|
||||
const valueTypes = ["int", "float", "date"];
|
||||
const classTypes = ["boolean", "string"];
|
||||
const chartData = ref({});
|
||||
const chartOptions = ref({});
|
||||
const chartComplete = ref(null); // Rendered chart.js instance data
|
||||
@@ -162,16 +349,19 @@ const startMaxDate = ref(null);
|
||||
const endMinDate = ref(null);
|
||||
const endMaxDate = ref(null);
|
||||
const valueStart = ref(null); // PrimeVue InputNumber v-model
|
||||
const valueEnd = ref(null); // PrimeVue InputNumber v-model
|
||||
const valueEnd = ref(null); // PrimeVue InputNumber v-model
|
||||
const valueStartMin = ref(null);
|
||||
const valueStartMax = ref(null);
|
||||
const valueEndMin = ref(null);
|
||||
const valueEndMax = ref(null);
|
||||
const tableClass = 'w-full h-full !border-separate !border-spacing-x-2 !table-auto text-sm';
|
||||
const headerModeClass = 'w-8 !p-2 !bg-neutral-10 !border-neutral-500 sticky top-0 left-0 z-10 bg-neutral-10';
|
||||
const headerClass = '!bg-neutral-10 !border-neutral-500 !py-2 sticky top-0 left-0 z-10 bg-neutral-10';
|
||||
const bodyModeClass = '!p-2 !border-0';
|
||||
const bodyClass = 'break-words !py-2 !border-0';
|
||||
const tableClass =
|
||||
"w-full h-full !border-separate !border-spacing-x-2 !table-auto text-sm";
|
||||
const headerModeClass =
|
||||
"w-8 !p-2 !bg-neutral-10 !border-neutral-500 sticky top-0 left-0 z-10 bg-neutral-10";
|
||||
const headerClass =
|
||||
"!bg-neutral-10 !border-neutral-500 !py-2 sticky top-0 left-0 z-10 bg-neutral-10";
|
||||
const bodyModeClass = "!p-2 !border-0";
|
||||
const bodyClass = "break-words !py-2 !border-0";
|
||||
const panelProps = {
|
||||
onClick: (event) => {
|
||||
event.stopPropagation();
|
||||
@@ -179,8 +369,9 @@ const panelProps = {
|
||||
};
|
||||
const tooltip = {
|
||||
attributeName: {
|
||||
value: 'Attributes with too many discrete values are excluded from selection. But users can still view those attributes in the DATA page.',
|
||||
class: '!max-w-[212px] !text-[10px] !opacity-90',
|
||||
value:
|
||||
"Attributes with too many discrete values are excluded from selection. But users can still view those attributes in the DATA page.",
|
||||
class: "!max-w-[212px] !text-[10px] !opacity-90",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -194,7 +385,7 @@ const attRangeTotal = computed(() => {
|
||||
let result = null; // Initialize the result variable with null
|
||||
|
||||
if (classTypes.includes(type) && attRangeData.value) {
|
||||
result = `(${attRangeData.value.length})`; // Assign the length of attRangeData if it exists
|
||||
result = `(${attRangeData.value.length})`; // Assign the length of attRangeData if it exists
|
||||
}
|
||||
return result;
|
||||
});
|
||||
@@ -202,7 +393,9 @@ const attRangeTotal = computed(() => {
|
||||
const attRangeData = computed(() => {
|
||||
let data = [];
|
||||
const type = selectedAttName.value.type;
|
||||
const sum = selectedAttName.value.options.map(item => item.freq).reduce((acc, cur) => acc + cur, 0);
|
||||
const sum = selectedAttName.value.options
|
||||
.map((item) => item.freq)
|
||||
.reduce((acc, cur) => acc + cur, 0);
|
||||
data = selectedAttName.value.options.map((item, index) => {
|
||||
const ratio = item.freq / sum;
|
||||
const result = {
|
||||
@@ -211,27 +404,31 @@ const attRangeData = computed(() => {
|
||||
type: type,
|
||||
value: item.value,
|
||||
occ_progress_bar: ratio * 100,
|
||||
occ_value: item.freq.toLocaleString('en-US'),
|
||||
occ_value: item.freq.toLocaleString("en-US"),
|
||||
occ_ratio: getPercentLabel(ratio),
|
||||
freq: item.freq
|
||||
freq: item.freq,
|
||||
};
|
||||
result.label = null;
|
||||
if (type === 'boolean') {
|
||||
result.label = item.value ? 'Yes' : 'No';
|
||||
if (type === "boolean") {
|
||||
result.label = item.value ? "Yes" : "No";
|
||||
} else {
|
||||
result.label = null;
|
||||
}
|
||||
return result;
|
||||
})
|
||||
});
|
||||
return data.sort((x, y) => y.freq - x.freq);
|
||||
});
|
||||
|
||||
// Get the selected Attribute radio's numeric-type data
|
||||
const valueData = computed(() => {
|
||||
// filter returns an array, find returns the first matched element, so use find here.
|
||||
if(valueTypes.includes(selectedAttName.value.type)){
|
||||
const data = filterAttrs.value.find(item => item.type === selectedAttName.value.type && item.key === selectedAttName.value.key);
|
||||
return data
|
||||
if (valueTypes.includes(selectedAttName.value.type)) {
|
||||
const data = filterAttrs.value.find(
|
||||
(item) =>
|
||||
item.type === selectedAttName.value.type &&
|
||||
item.key === selectedAttName.value.key,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -243,8 +440,8 @@ const sliderDataComputed = computed(() => {
|
||||
const max = valueData.value.max;
|
||||
const type = valueData.value.type;
|
||||
switch (type) {
|
||||
case 'dummy':
|
||||
case 'date':
|
||||
case "dummy":
|
||||
case "date":
|
||||
xAxisMin = new Date(min).getTime();
|
||||
xAxisMax = new Date(max).getTime();
|
||||
break;
|
||||
@@ -255,25 +452,25 @@ const sliderDataComputed = computed(() => {
|
||||
}
|
||||
const range = xAxisMax - xAxisMin;
|
||||
const step = range / selectRange.value;
|
||||
let data = []
|
||||
let data = [];
|
||||
|
||||
for (let i = 0; i <= selectRange.value; i++) {
|
||||
data.push(xAxisMin + (step * i));
|
||||
data.push(xAxisMin + step * i);
|
||||
}
|
||||
switch (type) {
|
||||
case 'int':
|
||||
data = data.map(value => {
|
||||
case "int":
|
||||
data = data.map((value) => {
|
||||
let result = Math.round(value);
|
||||
result = result === -0 ? 0 : result;
|
||||
return result;
|
||||
});
|
||||
break;
|
||||
case 'float':
|
||||
data = data.map(value => {
|
||||
case "float":
|
||||
data = data.map((value) => {
|
||||
let result = new Decimal(value.toFixed(2)).toNumber();
|
||||
result = result === -0 ? 0 : result;
|
||||
return result;
|
||||
})
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -288,25 +485,26 @@ const attValueTypeStartEnd = computed(() => {
|
||||
const type = selectedAttName.value.type;
|
||||
|
||||
switch (type) {
|
||||
case 'dummy': //sonar-qube
|
||||
case 'date':
|
||||
start = getMoment(startTime.value).format('YYYY-MM-DDTHH:mm:00');
|
||||
end = getMoment(endTime.value).format('YYYY-MM-DDTHH:mm:00');
|
||||
case "dummy": //sonar-qube
|
||||
case "date":
|
||||
start = getMoment(startTime.value).format("YYYY-MM-DDTHH:mm:00");
|
||||
end = getMoment(endTime.value).format("YYYY-MM-DDTHH:mm:00");
|
||||
break;
|
||||
default:
|
||||
start = valueStart.value;
|
||||
end = valueEnd.value;
|
||||
break;
|
||||
}
|
||||
const data = { // Data to send to the backend
|
||||
const data = {
|
||||
// Data to send to the backend
|
||||
type: type,
|
||||
data: {
|
||||
key: selectedAttName.value.key,
|
||||
min: start,
|
||||
max: end,
|
||||
}
|
||||
}
|
||||
emit('select-attribute', data);
|
||||
},
|
||||
};
|
||||
emit("select-attribute", data);
|
||||
|
||||
return [start, end];
|
||||
});
|
||||
@@ -317,7 +515,7 @@ const labelsData = computed(() => {
|
||||
const numPoints = 11;
|
||||
const step = (max - min) / (numPoints - 1);
|
||||
const data = [];
|
||||
for(let i = 0; i< numPoints; i++) {
|
||||
for (let i = 0; i < numPoints; i++) {
|
||||
const x = min + i * step;
|
||||
data.push(x);
|
||||
}
|
||||
@@ -333,7 +531,7 @@ function onRowSelect() {
|
||||
type: type,
|
||||
data: selectedAttRange.value,
|
||||
};
|
||||
emit('select-attribute', data);
|
||||
emit("select-attribute", data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -345,7 +543,7 @@ function onRowUnselect() {
|
||||
type: type,
|
||||
data: selectedAttRange.value,
|
||||
};
|
||||
emit('select-attribute', data);
|
||||
emit("select-attribute", data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -359,7 +557,7 @@ function onRowSelectAll(e) {
|
||||
type: type,
|
||||
data: selectedAttRange.value,
|
||||
};
|
||||
emit('select-attribute', data);
|
||||
emit("select-attribute", data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -372,7 +570,7 @@ function onRowUnelectAll() {
|
||||
type: type,
|
||||
data: selectedAttRange.value,
|
||||
};
|
||||
emit('select-attribute', data)
|
||||
emit("select-attribute", data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -385,14 +583,15 @@ function switchAttNameRadio(e) {
|
||||
endTime.value = null;
|
||||
valueStart.value = null;
|
||||
valueEnd.value = null;
|
||||
if(valueData.value) { // Switch Attribute Name
|
||||
if (valueData.value) {
|
||||
// Switch Attribute Name
|
||||
// Initialize two-way bindings
|
||||
selectArea.value = [0, selectRange.value];
|
||||
const min = valueData.value.min;
|
||||
const max = valueData.value.max;
|
||||
switch (selectedAttName.value.type) {
|
||||
case 'dummy': //sonar-qube
|
||||
case 'date':
|
||||
case "dummy": //sonar-qube
|
||||
case "date":
|
||||
// Clear two-way bindings except for date
|
||||
valueStart.value = null;
|
||||
valueEnd.value = null;
|
||||
@@ -431,8 +630,8 @@ function switchAttNameRadio(e) {
|
||||
* @param {number} value - The percentage value.
|
||||
* @returns {string} The CSS width style string.
|
||||
*/
|
||||
function progressWidth(value){
|
||||
return `width:${value}%;`
|
||||
function progressWidth(value) {
|
||||
return `width:${value}%;`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -440,8 +639,8 @@ function progressWidth(value){
|
||||
* @param {number} val - The raw ratio value.
|
||||
* @returns {string} The formatted percentage string.
|
||||
*/
|
||||
function getPercentLabel(val){
|
||||
if((val * 100).toFixed(1) >= 100) return `100%`;
|
||||
function getPercentLabel(val) {
|
||||
if ((val * 100).toFixed(1) >= 100) return `100%`;
|
||||
else return `${(val * 100).toFixed(1)}%`;
|
||||
}
|
||||
|
||||
@@ -461,7 +660,7 @@ function resizeMask(chart) {
|
||||
* @param {object} chart - The Chart.js instance data.
|
||||
*/
|
||||
function resizeLeftMask(chart, from) {
|
||||
const canvas = document.querySelector('#chartCanvasId canvas');
|
||||
const canvas = document.querySelector("#chartCanvasId canvas");
|
||||
const mask = document.getElementById("chart-mask-left");
|
||||
mask.style.left = `${canvas.offsetLeft + chart.chartArea.left}px`;
|
||||
mask.style.width = `${chart.chartArea.width * from}px`;
|
||||
@@ -474,7 +673,7 @@ function resizeLeftMask(chart, from) {
|
||||
* @param {object} chart - The Chart.js instance data.
|
||||
*/
|
||||
function resizeRightMask(chart, to) {
|
||||
const canvas = document.querySelector('#chartCanvasId canvas');
|
||||
const canvas = document.querySelector("#chartCanvasId canvas");
|
||||
const mask = document.getElementById("chart-mask-right");
|
||||
mask.style.left = `${canvas.offsetLeft + chart.chartArea.left + chart.chartArea.width * to}px`;
|
||||
mask.style.width = `${chart.chartArea.width * (1 - to)}px`;
|
||||
@@ -488,33 +687,37 @@ function resizeRightMask(chart, to) {
|
||||
function createChart() {
|
||||
const vData = valueData.value;
|
||||
const max = vData.chart.y_axis.max * 1.1;
|
||||
const data = setLineChartData(vData.chart.data, vData.chart.x_axis.max, vData.chart.x_axis.min);
|
||||
const isDateType = vData.type === 'date';
|
||||
const data = setLineChartData(
|
||||
vData.chart.data,
|
||||
vData.chart.x_axis.max,
|
||||
vData.chart.x_axis.min,
|
||||
);
|
||||
const isDateType = vData.type === "date";
|
||||
const minX = vData.chart.x_axis.min;
|
||||
const maxX = vData.chart.x_axis.max;
|
||||
let setChartData= {};
|
||||
let setChartOptions= {};
|
||||
let setChartData = {};
|
||||
let setChartOptions = {};
|
||||
let setLabels = [];
|
||||
|
||||
switch (vData.type) {
|
||||
case 'int':
|
||||
setLabels = data.map(item => Math.round(item.x));
|
||||
case "int":
|
||||
setLabels = data.map((item) => Math.round(item.x));
|
||||
break;
|
||||
case 'float':
|
||||
case "float":
|
||||
setLabels = data.map((item, index) => {
|
||||
let x;
|
||||
if (index === 0) {
|
||||
x = Math.floor(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;
|
||||
item.x = Math.ceil(item.x * 100) / 100;
|
||||
x = item.x;
|
||||
} else {
|
||||
x = Math.round(item.x * 100) / 100;
|
||||
x = Math.round(item.x * 100) / 100;
|
||||
}
|
||||
return x
|
||||
return x;
|
||||
});
|
||||
break;
|
||||
case 'date':
|
||||
case "date":
|
||||
setLabels = labelsData.value;
|
||||
break;
|
||||
default:
|
||||
@@ -523,14 +726,14 @@ function createChart() {
|
||||
setChartData = {
|
||||
datasets: [
|
||||
{
|
||||
label: 'Attribute Value',
|
||||
label: "Attribute Value",
|
||||
data: data,
|
||||
fill: 'start',
|
||||
fill: "start",
|
||||
showLine: false,
|
||||
tension: 0.4,
|
||||
backgroundColor: 'rgba(0,153,255)',
|
||||
backgroundColor: "rgba(0,153,255)",
|
||||
pointRadius: 0,
|
||||
}
|
||||
},
|
||||
],
|
||||
labels: setLabels,
|
||||
};
|
||||
@@ -542,20 +745,20 @@ function createChart() {
|
||||
top: 16,
|
||||
left: 8,
|
||||
right: 8,
|
||||
}
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
legend: false, // Hide legend
|
||||
filler: {
|
||||
propagate: false
|
||||
propagate: false,
|
||||
},
|
||||
title: false
|
||||
title: false,
|
||||
},
|
||||
animation: {
|
||||
onComplete: e => {
|
||||
onComplete: (e) => {
|
||||
chartComplete.value = e.chart;
|
||||
resizeMask(e.chart);
|
||||
}
|
||||
},
|
||||
},
|
||||
interaction: {
|
||||
intersect: true,
|
||||
@@ -564,59 +767,60 @@ function createChart() {
|
||||
y: {
|
||||
beginAtZero: true, // Scale includes 0
|
||||
max: max,
|
||||
ticks: { // Set tick intervals
|
||||
ticks: {
|
||||
// Set tick intervals
|
||||
display: false, // Hide values, only show grid lines
|
||||
stepSize: max / 4,
|
||||
},
|
||||
grid: {
|
||||
color: 'rgba(100,116,139)',
|
||||
color: "rgba(100,116,139)",
|
||||
z: 1,
|
||||
},
|
||||
border: {
|
||||
display: false, // Hide the extra border line on the left
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
if(isDateType) {
|
||||
if (isDateType) {
|
||||
setChartOptions.scales.x = {
|
||||
type: 'time',
|
||||
type: "time",
|
||||
ticks: {
|
||||
min: minX,
|
||||
max: maxX,
|
||||
autoSkip: true, // Automatically determine whether to convert time units
|
||||
maxRotation: 0, // Do not rotate labels (0~50)
|
||||
color: '#334155',
|
||||
color: "#334155",
|
||||
display: true,
|
||||
source: 'labels', // Flexibly display label count proportionally
|
||||
source: "labels", // Flexibly display label count proportionally
|
||||
},
|
||||
grid: {
|
||||
display: false, // Hide x-axis grid lines
|
||||
},
|
||||
time: {
|
||||
minUnit: 'day', // Minimum display unit
|
||||
minUnit: "day", // Minimum display unit
|
||||
// displayFormats: {
|
||||
// minute: 'HH:mm MMM d',
|
||||
// hour: 'HH:mm MMM d',
|
||||
// }
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
} else {
|
||||
setChartOptions.scales.x = {
|
||||
bounds: 'data',
|
||||
type: 'linear',
|
||||
bounds: "data",
|
||||
type: "linear",
|
||||
min: minX,
|
||||
max: maxX,
|
||||
ticks: {
|
||||
autoSkip: true,
|
||||
maxRotation: 0, // Do not rotate labels (0~50)
|
||||
color: '#334155',
|
||||
callback: ((value, index, values) => {
|
||||
color: "#334155",
|
||||
callback: (value, index, values) => {
|
||||
let x;
|
||||
switch (vData.type) {
|
||||
case 'int':
|
||||
case "int":
|
||||
return Math.round(value);
|
||||
case 'float':
|
||||
case "float":
|
||||
switch (index) {
|
||||
case 0:
|
||||
x = Math.floor(value * 100) / 100;
|
||||
@@ -629,15 +833,17 @@ function createChart() {
|
||||
}
|
||||
// Handle scientific notation and other format conversions
|
||||
// Decimal cannot handle numbers exceeding 16 digits
|
||||
x = new Intl.NumberFormat(undefined, {useGrouping: false}).format(x);
|
||||
return x
|
||||
x = new Intl.NumberFormat(undefined, {
|
||||
useGrouping: false,
|
||||
}).format(x);
|
||||
return x;
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
display: false, // Hide x-axis grid lines
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
chartData.value = setChartData;
|
||||
chartOptions.value = setChartOptions;
|
||||
@@ -654,8 +860,8 @@ function changeSelectArea(e) {
|
||||
const end = sliderData[e[1].toFixed()]; // Get the index, which must be an integer.
|
||||
|
||||
switch (selectedAttName.value.type) {
|
||||
case 'dummy':
|
||||
case 'date':
|
||||
case "dummy":
|
||||
case "date":
|
||||
startTime.value = new Date(start);
|
||||
endTime.value = new Date(end);
|
||||
// Reset the start/end calendar selection range
|
||||
@@ -684,48 +890,56 @@ function changeSelectArea(e) {
|
||||
function sliderValueRange(e, direction) {
|
||||
// Find the closest index; time format: millisecond timestamps
|
||||
const sliderData = sliderDataComputed.value;
|
||||
const isDateType = selectedAttName.value.type === 'date';
|
||||
const isDateType = selectedAttName.value.type === "date";
|
||||
let targetTime = [];
|
||||
let inputValue;
|
||||
|
||||
if(isDateType) targetTime = [new Date(attValueTypeStartEnd.value[0]).getTime(), new Date(attValueTypeStartEnd.value[1]).getTime()];
|
||||
else targetTime = [attValueTypeStartEnd.value[0], attValueTypeStartEnd.value[1]]
|
||||
if (isDateType)
|
||||
targetTime = [
|
||||
new Date(attValueTypeStartEnd.value[0]).getTime(),
|
||||
new Date(attValueTypeStartEnd.value[1]).getTime(),
|
||||
];
|
||||
else
|
||||
targetTime = [attValueTypeStartEnd.value[0], attValueTypeStartEnd.value[1]];
|
||||
|
||||
const closestIndexes = targetTime.map(target => {
|
||||
const closestIndexes = targetTime.map((target) => {
|
||||
let closestIndex = 0;
|
||||
closestIndex = ((target - sliderData[0])/(sliderData[sliderData.length-1]-sliderData[0])) * sliderData.length;
|
||||
closestIndex =
|
||||
((target - sliderData[0]) /
|
||||
(sliderData[sliderData.length - 1] - sliderData[0])) *
|
||||
sliderData.length;
|
||||
let result = Math.round(Math.abs(closestIndex));
|
||||
result = result > selectRange.value ? selectRange.value : result;
|
||||
return result
|
||||
return result;
|
||||
});
|
||||
// Update the slider
|
||||
selectArea.value = closestIndexes;
|
||||
// Reset the start/end calendar selection range
|
||||
if(!isDateType) inputValue = Number(e.value.replace(/,/g, '')) ;
|
||||
if(direction === 'start') {
|
||||
if(isDateType){
|
||||
if (!isDateType) inputValue = Number(e.value.replace(/,/g, ""));
|
||||
if (direction === "start") {
|
||||
if (isDateType) {
|
||||
endMinDate.value = e;
|
||||
} else {
|
||||
valueEndMin.value = inputValue;
|
||||
}
|
||||
}
|
||||
else if(direction === 'end') {
|
||||
if(isDateType) {
|
||||
} else if (direction === "end") {
|
||||
if (isDateType) {
|
||||
startMaxDate.value = e;
|
||||
} else {
|
||||
valueStartMax.value = inputValue;
|
||||
};
|
||||
}
|
||||
}
|
||||
// Recalculate the chart mask
|
||||
if(!isNaN(closestIndexes[0]) && !isNaN(closestIndexes[1])) resizeMask(chartComplete.value);
|
||||
if (!isNaN(closestIndexes[0]) && !isNaN(closestIndexes[1]))
|
||||
resizeMask(chartComplete.value);
|
||||
else return;
|
||||
}
|
||||
|
||||
// created() equivalent
|
||||
emitter.on('map-filter-reset', value => {
|
||||
if(value) {
|
||||
emitter.on("map-filter-reset", (value) => {
|
||||
if (value) {
|
||||
selectedAttRange.value = null;
|
||||
if(valueData.value && valueTypes.includes(selectedAttName.value.type)){
|
||||
if (valueData.value && valueTypes.includes(selectedAttName.value.type)) {
|
||||
const min = valueData.value.min;
|
||||
const max = valueData.value.max;
|
||||
startTime.value = new Date(min);
|
||||
@@ -745,12 +959,12 @@ onMounted(() => {
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
selectedAttName.value = {};
|
||||
emitter.off('map-filter-reset');
|
||||
emitter.off("map-filter-reset");
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
@reference "../../../../assets/tailwind.css";
|
||||
:deep(table tbody td:nth-child(2)) {
|
||||
@apply whitespace-nowrap break-keep overflow-hidden text-ellipsis max-w-0
|
||||
@apply whitespace-nowrap break-keep overflow-hidden text-ellipsis max-w-0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,41 +1,75 @@
|
||||
<template>
|
||||
<div class=" w-full h-full">
|
||||
<div class="h-[calc(100%_-_58px)] border-b border-neutral-400 mb-2 scrollbar overflow-x-hidden overflow-y-auto">
|
||||
<div v-if="this.temporaryData.length === 0" class="h-full flex justify-center items-center">
|
||||
<span class="text-neutral-500">No Filter.</span>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="text-primary h2 flex items-center justify-start my-4">
|
||||
<span class="material-symbols-outlined m-2">info</span>
|
||||
<p>Disabled filters will not be saved.</p>
|
||||
</div>
|
||||
<Timeline :value="ruleData">
|
||||
<template #content="rule">
|
||||
<div class="border-b border-neutral-300 flex justify-between items-center space-x-2">
|
||||
<!-- content -->
|
||||
<div class="pl-2 mb-2">
|
||||
<p class="text-sm font-medium leading-5">{{ rule.item.type }}: <span class="text-neutral-500">{{ rule.item.label }}</span></p>
|
||||
</div>
|
||||
<!-- button -->
|
||||
<div class="min-w-fit">
|
||||
<InputSwitch v-model="rule.item.toggle" @input="isRule($event, rule.index)"/>
|
||||
<button type="button" class="m-2 focus:ring focus:ring-danger/20 text-neutral-500 hover:text-danger" @click.stop="deleteRule(rule.index)">
|
||||
<span class="material-symbols-outlined">delete</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Timeline>
|
||||
</div>
|
||||
<div class="w-full h-full">
|
||||
<div
|
||||
class="h-[calc(100%_-_58px)] border-b border-neutral-400 mb-2 scrollbar overflow-x-hidden overflow-y-auto"
|
||||
>
|
||||
<div
|
||||
v-if="this.temporaryData.length === 0"
|
||||
class="h-full flex justify-center items-center"
|
||||
>
|
||||
<span class="text-neutral-500">No Filter.</span>
|
||||
</div>
|
||||
<!-- Button -->
|
||||
<div>
|
||||
<div class="float-right space-x-4 px-4 py-2">
|
||||
<button type="button" class="btn btn-sm " :class="[ temporaryData.length === 0 ? 'btn-disable' : 'btn-neutral']" :disabled="temporaryData.length === 0" @click="deleteRule('all')">Delete All</button>
|
||||
<button type="button" class="btn btn-sm" :class="[ temporaryData.length === 0 ? 'btn-disable' : 'btn-neutral']" :disabled="temporaryData.length === 0" @click="submitAll">Apply All</button>
|
||||
<div v-else>
|
||||
<div class="text-primary h2 flex items-center justify-start my-4">
|
||||
<span class="material-symbols-outlined m-2">info</span>
|
||||
<p>Disabled filters will not be saved.</p>
|
||||
</div>
|
||||
<Timeline :value="ruleData">
|
||||
<template #content="rule">
|
||||
<div
|
||||
class="border-b border-neutral-300 flex justify-between items-center space-x-2"
|
||||
>
|
||||
<!-- content -->
|
||||
<div class="pl-2 mb-2">
|
||||
<p class="text-sm font-medium leading-5">
|
||||
{{ rule.item.type }}: <span class="text-neutral-500">{{
|
||||
rule.item.label
|
||||
}}</span>
|
||||
</p>
|
||||
</div>
|
||||
<!-- button -->
|
||||
<div class="min-w-fit">
|
||||
<InputSwitch
|
||||
v-model="rule.item.toggle"
|
||||
@input="isRule($event, rule.index)"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="m-2 focus:ring focus:ring-danger/20 text-neutral-500 hover:text-danger"
|
||||
@click.stop="deleteRule(rule.index)"
|
||||
>
|
||||
<span class="material-symbols-outlined">delete</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Timeline>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Button -->
|
||||
<div>
|
||||
<div class="float-right space-x-4 px-4 py-2">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm"
|
||||
:class="[temporaryData.length === 0 ? 'btn-disable' : 'btn-neutral']"
|
||||
:disabled="temporaryData.length === 0"
|
||||
@click="deleteRule('all')"
|
||||
>
|
||||
Delete All
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm"
|
||||
:class="[temporaryData.length === 0 ? 'btn-disable' : 'btn-neutral']"
|
||||
:disabled="temporaryData.length === 0"
|
||||
@click="submitAll"
|
||||
>
|
||||
Apply All
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -49,30 +83,37 @@
|
||||
* apply-all actions.
|
||||
*/
|
||||
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useToast } from 'vue-toast-notification';
|
||||
import { useLoadingStore } from '@/stores/loading';
|
||||
import { useAllMapDataStore } from '@/stores/allMapData';
|
||||
import { delaySecond, } from '@/utils/timeUtil.js';
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useToast } from "vue-toast-notification";
|
||||
import { useLoadingStore } from "@/stores/loading";
|
||||
import { useAllMapDataStore } from "@/stores/allMapData";
|
||||
import { delaySecond } from "@/utils/timeUtil.js";
|
||||
|
||||
const emit = defineEmits(['submit-all']);
|
||||
const emit = defineEmits(["submit-all"]);
|
||||
const $toast = useToast();
|
||||
|
||||
const loadingStore = useLoadingStore();
|
||||
const allMapDataStore = useAllMapDataStore();
|
||||
const { isLoading } = storeToRefs(loadingStore);
|
||||
const { hasResultRule, temporaryData, postRuleData, ruleData, isRuleData, tempFilterId } = storeToRefs(allMapDataStore);
|
||||
const {
|
||||
hasResultRule,
|
||||
temporaryData,
|
||||
postRuleData,
|
||||
ruleData,
|
||||
isRuleData,
|
||||
tempFilterId,
|
||||
} = storeToRefs(allMapDataStore);
|
||||
|
||||
/**
|
||||
* Toggles a filter rule on or off.
|
||||
* @param {boolean} e - Whether the rule is enabled.
|
||||
* @param {number} index - The rule index.
|
||||
*/
|
||||
function isRule(e, index){
|
||||
function isRule(e, index) {
|
||||
const rule = isRuleData.value[index];
|
||||
// First get the rule object
|
||||
// To preserve data order, set the value to 0 and remove it during submitAll
|
||||
if(!e) temporaryData.value[index] = 0;
|
||||
if (!e) temporaryData.value[index] = 0;
|
||||
else temporaryData.value[index] = rule;
|
||||
}
|
||||
|
||||
@@ -81,20 +122,20 @@ function isRule(e, index){
|
||||
* @param {number|string} index - The rule index, or 'all' to delete all.
|
||||
*/
|
||||
async function deleteRule(index) {
|
||||
if(index === 'all') {
|
||||
if (index === "all") {
|
||||
temporaryData.value = [];
|
||||
isRuleData.value = [];
|
||||
ruleData.value = [];
|
||||
if(tempFilterId.value) {
|
||||
if (tempFilterId.value) {
|
||||
isLoading.value = true;
|
||||
tempFilterId.value = await null;
|
||||
await allMapDataStore.getAllMapData();
|
||||
await allMapDataStore.getAllTrace(); // SidebarTrace needs to update in sync
|
||||
await emit('submit-all');
|
||||
await emit("submit-all");
|
||||
isLoading.value = false;
|
||||
}
|
||||
$toast.success('Filter(s) deleted.');
|
||||
}else{
|
||||
$toast.success("Filter(s) deleted.");
|
||||
} else {
|
||||
$toast.success(`Filter deleted.`);
|
||||
temporaryData.value.splice(index, 1);
|
||||
isRuleData.value.splice(index, 1);
|
||||
@@ -104,23 +145,23 @@ async function deleteRule(index) {
|
||||
|
||||
/** Submits all enabled filter rules and refreshes the map data. */
|
||||
async function submitAll() {
|
||||
postRuleData.value = temporaryData.value.filter(item => item !== 0); // Get submit data; if toggle buttons are used, find and remove items set to 0
|
||||
if(!postRuleData.value?.length) return $toast.error('Not selected');
|
||||
postRuleData.value = temporaryData.value.filter((item) => item !== 0); // Get submit data; if toggle buttons are used, find and remove items set to 0
|
||||
if (!postRuleData.value?.length) return $toast.error("Not selected");
|
||||
await allMapDataStore.checkHasResult(); // Quick backend check for results
|
||||
|
||||
if(hasResultRule.value === null) {
|
||||
if (hasResultRule.value === null) {
|
||||
return;
|
||||
} else if(hasResultRule.value) {
|
||||
} else if (hasResultRule.value) {
|
||||
isLoading.value = true;
|
||||
await allMapDataStore.addTempFilterId();
|
||||
await allMapDataStore.getAllMapData();
|
||||
await allMapDataStore.getAllTrace(); // SidebarTrace needs to update in sync
|
||||
if(temporaryData.value[0]?.type) {
|
||||
if (temporaryData.value[0]?.type) {
|
||||
allMapDataStore.traceId = await allMapDataStore.traces[0]?.id;
|
||||
}
|
||||
await emit('submit-all');
|
||||
await emit("submit-all");
|
||||
isLoading.value = false;
|
||||
$toast.success('Filter(s) applied.');
|
||||
$toast.success("Filter(s) applied.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -128,7 +169,7 @@ async function submitAll() {
|
||||
isLoading.value = true;
|
||||
await delaySecond(1);
|
||||
isLoading.value = false;
|
||||
$toast.warning('No result.');
|
||||
$toast.warning("No result.");
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -136,21 +177,21 @@ async function submitAll() {
|
||||
@reference "../../../../assets/tailwind.css";
|
||||
/* TimeLine */
|
||||
:deep(.p-timeline) {
|
||||
@apply leading-none my-4
|
||||
@apply leading-none my-4;
|
||||
}
|
||||
:deep(.p-timeline-event-opposite) {
|
||||
@apply hidden
|
||||
@apply hidden;
|
||||
}
|
||||
:deep(.p-timeline-event-separator) {
|
||||
@apply mx-4
|
||||
@apply mx-4;
|
||||
}
|
||||
:deep(.p-timeline-event-marker) {
|
||||
@apply !bg-primary !border-primary !w-2 !h-2
|
||||
@apply !bg-primary !border-primary !w-2 !h-2;
|
||||
}
|
||||
:deep(.p-timeline-event-connector) {
|
||||
@apply !bg-primary my-2 !w-[1px]
|
||||
@apply !bg-primary my-2 !w-[1px];
|
||||
}
|
||||
:deep(.p-timeline-event-content) {
|
||||
@apply !px-0
|
||||
@apply !px-0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<template>
|
||||
<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"
|
||||
>
|
||||
<section class="pt-2 pb-20 space-y-2 text-sm min-w-[48%] h-full">
|
||||
<p class="h2">Range Selection</p>
|
||||
<div class="text-primary h2 flex items-center justify-start">
|
||||
@@ -12,18 +14,48 @@
|
||||
<div id="chart-mask-right" class="absolute bg-neutral-10/50"></div>
|
||||
</div>
|
||||
<div class="px-2 py-3">
|
||||
<Slider v-model="selectArea" :step="1" :min="0" :max="selectRange" range class="mx-2" @change="changeSelectArea($event)"/>
|
||||
<Slider
|
||||
v-model="selectArea"
|
||||
:step="1"
|
||||
:min="0"
|
||||
:max="selectRange"
|
||||
range
|
||||
class="mx-2"
|
||||
@change="changeSelectArea($event)"
|
||||
/>
|
||||
</div>
|
||||
<!-- Calendar group -->
|
||||
<div class="flex justify-center items-center space-x-2 w-full">
|
||||
<div>
|
||||
<span class="block mb-2">Start time</span>
|
||||
<Calendar v-model="startTime" dateFormat="yy/mm/dd" :panelProps="panelProps" :minDate="startMinDate" :maxDate="startMaxDate" showTime showIcon hourFormat="24" @date-select="sliderTimeRange($event, 'start')" id="startCalendar"/>
|
||||
<Calendar
|
||||
v-model="startTime"
|
||||
dateFormat="yy/mm/dd"
|
||||
:panelProps="panelProps"
|
||||
:minDate="startMinDate"
|
||||
:maxDate="startMaxDate"
|
||||
showTime
|
||||
showIcon
|
||||
hourFormat="24"
|
||||
@date-select="sliderTimeRange($event, 'start')"
|
||||
id="startCalendar"
|
||||
/>
|
||||
</div>
|
||||
<span class="block mt-4">~</span>
|
||||
<div>
|
||||
<span class="block mb-2">End time</span>
|
||||
<Calendar v-model="endTime" dateFormat="yy/mm/dd" :panelProps="panelProps" :minDate="endMinDate" :maxDate="endMaxDate" showTime showIcon hourFormat="24" @date-select="sliderTimeRange($event, 'end')" id="endCalendar"/>
|
||||
<Calendar
|
||||
v-model="endTime"
|
||||
dateFormat="yy/mm/dd"
|
||||
:panelProps="panelProps"
|
||||
:minDate="endMinDate"
|
||||
:maxDate="endMaxDate"
|
||||
showTime
|
||||
showIcon
|
||||
hourFormat="24"
|
||||
@date-select="sliderTimeRange($event, 'end')"
|
||||
id="endCalendar"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End calendar group -->
|
||||
@@ -41,14 +73,14 @@
|
||||
* duration range selectors.
|
||||
*/
|
||||
|
||||
import { ref, computed, watch, onMounted } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useAllMapDataStore } from '@/stores/allMapData';
|
||||
import { Chart, registerables } from 'chart.js';
|
||||
import 'chartjs-adapter-moment';
|
||||
import getMoment from 'moment';
|
||||
import { ref, computed, watch, onMounted } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useAllMapDataStore } from "@/stores/allMapData";
|
||||
import { Chart, registerables } from "chart.js";
|
||||
import "chartjs-adapter-moment";
|
||||
import getMoment from "moment";
|
||||
|
||||
const props = defineProps(['selectValue']);
|
||||
const props = defineProps(["selectValue"]);
|
||||
|
||||
const allMapDataStore = useAllMapDataStore();
|
||||
const { filterTimeframe, selectTimeFrame } = storeToRefs(allMapDataStore);
|
||||
@@ -71,8 +103,8 @@ const panelProps = ref({
|
||||
|
||||
// user select time start and end
|
||||
const timeFrameStartEnd = computed(() => {
|
||||
const start = getMoment(startTime.value).format('YYYY-MM-DDTHH:mm:00');
|
||||
const end = getMoment(endTime.value).format('YYYY-MM-DDTHH:mm:00');
|
||||
const start = getMoment(startTime.value).format("YYYY-MM-DDTHH:mm:00");
|
||||
const end = getMoment(endTime.value).format("YYYY-MM-DDTHH:mm:00");
|
||||
selectTimeFrame.value = [start, end]; // Data to send to the backend
|
||||
|
||||
return [start, end];
|
||||
@@ -84,10 +116,10 @@ const sliderData = computed(() => {
|
||||
const xAxisMax = new Date(filterTimeframe.value.x_axis.max).getTime();
|
||||
const range = xAxisMax - xAxisMin;
|
||||
const step = range / selectRange.value;
|
||||
const data = []
|
||||
const data = [];
|
||||
|
||||
for (let i = 0; i <= selectRange.value; i++) {
|
||||
data.push(xAxisMin + (step * i));
|
||||
data.push(xAxisMin + step * i);
|
||||
}
|
||||
|
||||
return data;
|
||||
@@ -95,7 +127,7 @@ const sliderData = computed(() => {
|
||||
|
||||
// Add the minimum and maximum values
|
||||
const timeFrameData = computed(() => {
|
||||
const data = filterTimeframe.value.data.map(i=>({x:i.x,y:i.y}))
|
||||
const data = filterTimeframe.value.data.map((i) => ({ x: i.x, y: i.y }));
|
||||
// See ./public/timeFrameSlope for the y-axis slope calculation diagram
|
||||
// x values are 0 ~ 11,
|
||||
// Name three coordinates (ax, ay), (bx, by), (cx, cy) as (a, b), (c, d), (e, f)
|
||||
@@ -109,8 +141,8 @@ const timeFrameData = computed(() => {
|
||||
const d = filterTimeframe.value.data[0].y;
|
||||
const e = 2;
|
||||
const f = filterTimeframe.value.data[1].y;
|
||||
b = (e*d - a*d - f*a - f*c) / (e - c - a);
|
||||
if(b < 0) {
|
||||
b = (e * d - a * d - f * a - f * c) / (e - c - a);
|
||||
if (b < 0) {
|
||||
b = 0;
|
||||
}
|
||||
// Y-axis maximum value
|
||||
@@ -119,8 +151,8 @@ const timeFrameData = computed(() => {
|
||||
const mc = 10;
|
||||
const md = filterTimeframe.value.data[9].y;
|
||||
const me = 11;
|
||||
let mf = (mb*me - mb*mc -md*me + md*ma) / (ma - mc);
|
||||
if(mf < 0) {
|
||||
let mf = (mb * me - mb * mc - md * me + md * ma) / (ma - mc);
|
||||
if (mf < 0) {
|
||||
mf = 0;
|
||||
}
|
||||
|
||||
@@ -128,12 +160,12 @@ const timeFrameData = computed(() => {
|
||||
data.unshift({
|
||||
x: filterTimeframe.value.x_axis.min_base,
|
||||
y: b,
|
||||
})
|
||||
});
|
||||
// Add the maximum value
|
||||
data.push({
|
||||
x: filterTimeframe.value.x_axis.max_base,
|
||||
y: mf,
|
||||
})
|
||||
});
|
||||
|
||||
return data;
|
||||
});
|
||||
@@ -144,7 +176,7 @@ const labelsData = computed(() => {
|
||||
const numPoints = 11;
|
||||
const step = (max - min) / (numPoints - 1);
|
||||
const data = [];
|
||||
for(let i = 0; i< numPoints; i++) {
|
||||
for (let i = 0; i < numPoints; i++) {
|
||||
const x = min + i * step;
|
||||
data.push(x);
|
||||
}
|
||||
@@ -152,7 +184,7 @@ const labelsData = computed(() => {
|
||||
});
|
||||
|
||||
watch(selectTimeFrame, (newValue, oldValue) => {
|
||||
if(newValue.length === 0) {
|
||||
if (newValue.length === 0) {
|
||||
startTime.value = new Date(filterTimeframe.value.x_axis.min);
|
||||
endTime.value = new Date(filterTimeframe.value.x_axis.max);
|
||||
selectArea.value = [0, selectRange.value];
|
||||
@@ -167,7 +199,7 @@ watch(selectTimeFrame, (newValue, oldValue) => {
|
||||
function resizeMask(chartInstance) {
|
||||
const from = (selectArea.value[0] * 0.01) / (selectRange.value * 0.01);
|
||||
const to = (selectArea.value[1] * 0.01) / (selectRange.value * 0.01);
|
||||
if(props.selectValue[0] === 'Timeframes') {
|
||||
if (props.selectValue[0] === "Timeframes") {
|
||||
resizeLeftMask(chartInstance, from);
|
||||
resizeRightMask(chartInstance, to);
|
||||
}
|
||||
@@ -208,20 +240,20 @@ function createChart() {
|
||||
const maxX = timeFrameData.value[timeFrameData.value.length - 1]?.x;
|
||||
|
||||
const data = {
|
||||
labels:labelsData.value,
|
||||
labels: labelsData.value,
|
||||
datasets: [
|
||||
{
|
||||
label: 'Case',
|
||||
label: "Case",
|
||||
data: timeFrameData.value,
|
||||
fill: 'start',
|
||||
fill: "start",
|
||||
showLine: false,
|
||||
tension: 0.4,
|
||||
backgroundColor: 'rgba(0,153,255)',
|
||||
backgroundColor: "rgba(0,153,255)",
|
||||
pointRadius: 0,
|
||||
x: 'x',
|
||||
y: 'y',
|
||||
}
|
||||
]
|
||||
x: "x",
|
||||
y: "y",
|
||||
},
|
||||
],
|
||||
};
|
||||
const options = {
|
||||
responsive: true,
|
||||
@@ -231,66 +263,67 @@ function createChart() {
|
||||
top: 16,
|
||||
left: 8,
|
||||
right: 8,
|
||||
}
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
legend: false, // Hide legend
|
||||
filler: {
|
||||
propagate: false
|
||||
propagate: false,
|
||||
},
|
||||
title: false
|
||||
title: false,
|
||||
},
|
||||
// animations: false, // Disable animations
|
||||
animation: {
|
||||
onComplete: e => {
|
||||
onComplete: (e) => {
|
||||
resizeMask(e.chart);
|
||||
}
|
||||
},
|
||||
},
|
||||
interaction: {
|
||||
intersect: true,
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
type: 'time',
|
||||
type: "time",
|
||||
min: minX,
|
||||
max: maxX,
|
||||
ticks: {
|
||||
autoSkip: true,
|
||||
maxRotation: 0, // Do not rotate labels (0~50)
|
||||
color: '#334155',
|
||||
color: "#334155",
|
||||
display: true,
|
||||
source: 'labels',
|
||||
source: "labels",
|
||||
},
|
||||
grid: {
|
||||
display: false, // Hide x-axis grid lines
|
||||
},
|
||||
time: {
|
||||
minUnit: 'day', // Minimum display unit
|
||||
minUnit: "day", // Minimum display unit
|
||||
// displayFormats: {
|
||||
// minute: 'HH:mm MMM d',
|
||||
// hour: 'HH:mm MMM d',
|
||||
// }
|
||||
}
|
||||
},
|
||||
},
|
||||
y: {
|
||||
beginAtZero: true, // Scale includes 0
|
||||
max: max,
|
||||
ticks: { // Set tick intervals
|
||||
ticks: {
|
||||
// Set tick intervals
|
||||
display: false, // Hide values, only show grid lines
|
||||
stepSize: max / 4,
|
||||
},
|
||||
grid: {
|
||||
color: 'rgba(100,116,139)',
|
||||
color: "rgba(100,116,139)",
|
||||
z: 1,
|
||||
},
|
||||
border: {
|
||||
display: false, // Hide the extra border line on the left
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const config = {
|
||||
type: 'line',
|
||||
const config = {
|
||||
type: "line",
|
||||
data: data,
|
||||
options: options,
|
||||
};
|
||||
@@ -327,22 +360,29 @@ function changeSelectArea(e) {
|
||||
function sliderTimeRange(e, direction) {
|
||||
// Find the closest index; time format: millisecond timestamps
|
||||
const sliderDataVal = sliderData.value;
|
||||
const targetTime = [new Date(timeFrameStartEnd.value[0]).getTime(), new Date(timeFrameStartEnd.value[1]).getTime()];
|
||||
const closestIndexes = targetTime.map(target => {
|
||||
const targetTime = [
|
||||
new Date(timeFrameStartEnd.value[0]).getTime(),
|
||||
new Date(timeFrameStartEnd.value[1]).getTime(),
|
||||
];
|
||||
const closestIndexes = targetTime.map((target) => {
|
||||
let closestIndex = 0;
|
||||
closestIndex = ((target - sliderDataVal[0])/(sliderDataVal[sliderDataVal.length-1]-sliderDataVal[0])) * sliderDataVal.length;
|
||||
closestIndex =
|
||||
((target - sliderDataVal[0]) /
|
||||
(sliderDataVal[sliderDataVal.length - 1] - sliderDataVal[0])) *
|
||||
sliderDataVal.length;
|
||||
let result = Math.round(Math.abs(closestIndex));
|
||||
result = result > selectRange.value ? selectRange.value : result;
|
||||
return result
|
||||
return result;
|
||||
});
|
||||
|
||||
// Update the slider
|
||||
selectArea.value = closestIndexes;
|
||||
// Reset the start/end calendar selection range
|
||||
if(direction === 'start') endMinDate.value = e;
|
||||
else if(direction === 'end') startMaxDate.value = e;
|
||||
if (direction === "start") endMinDate.value = e;
|
||||
else if (direction === "end") startMaxDate.value = e;
|
||||
// Recalculate the chart mask
|
||||
if(!isNaN(closestIndexes[0]) && !isNaN(closestIndexes[1])) resizeMask(chart.value);
|
||||
if (!isNaN(closestIndexes[0]) && !isNaN(closestIndexes[1]))
|
||||
resizeMask(chart.value);
|
||||
else return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<template>
|
||||
<div class="flex justify-between items-start bg-neutral-10 border border-neutral-300 rounded-xl px-4 w-full h-full space-x-4 overflow-y-auto overflow-x-auto scrollbar">
|
||||
<div
|
||||
class="flex justify-between items-start bg-neutral-10 border border-neutral-300 rounded-xl px-4 w-full h-full space-x-4 overflow-y-auto overflow-x-auto scrollbar"
|
||||
>
|
||||
<!-- Range Selection -->
|
||||
<section class="py-2 space-y-2 text-sm min-w-[48%] h-full">
|
||||
<p class="h2">Range Selection</p>
|
||||
@@ -7,32 +9,64 @@
|
||||
<span class="material-symbols-outlined mr-2 !text-base">info</span>
|
||||
<p>Select a percentage range.</p>
|
||||
</div>
|
||||
<Chart type="bar" :data="chartData" :options="chartOptions" class="h-2/5" />
|
||||
<Chart
|
||||
type="bar"
|
||||
:data="chartData"
|
||||
:options="chartOptions"
|
||||
class="h-2/5"
|
||||
/>
|
||||
<div class="px-2">
|
||||
<p class="py-4">Select percentage of case <span class=" float-right">{{ caseTotalPercent }}%</span></p>
|
||||
<Slider v-model="selectArea" :step="1" :min="0" :max="traceTotal" range class="mx-2" />
|
||||
<p class="py-4">
|
||||
Select percentage of case
|
||||
<span class="float-right">{{ caseTotalPercent }}%</span>
|
||||
</p>
|
||||
<Slider
|
||||
v-model="selectArea"
|
||||
:step="1"
|
||||
:min="0"
|
||||
:max="traceTotal"
|
||||
range
|
||||
class="mx-2"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
<!-- Trace List -->
|
||||
<section class="h-full min-w-[48%] py-2 space-y-2">
|
||||
<p class="h2">Trace List ({{ traceTotal }})</p>
|
||||
<p class="text-primary h2 flex items-center justify-start">
|
||||
<span class="material-symbols-outlined mr-2 !text-base">info</span>Click trace number to see more.
|
||||
<span class="material-symbols-outlined mr-2 !text-base">info</span>Click
|
||||
trace number to see more.
|
||||
</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">
|
||||
<caption class="hidden">Trace list</caption>
|
||||
<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>
|
||||
<th class="h2 px-2 border-b border-neutral-500 text-start" colspan="3">Occurrences</th>
|
||||
<th
|
||||
class="h2 px-2 border-b border-neutral-500 text-start"
|
||||
colspan="3"
|
||||
>
|
||||
Occurrences
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(trace, key) in traceList" :key="key" class=" cursor-pointer hover:text-primary" @click="switchCaseData(trace.id, trace.base_count)">
|
||||
<tr
|
||||
v-for="(trace, key) in traceList"
|
||||
:key="key"
|
||||
class="cursor-pointer hover:text-primary"
|
||||
@click="switchCaseData(trace.id, trace.base_count)"
|
||||
>
|
||||
<td class="p-2 text-center">#{{ trace.id }}</td>
|
||||
<td class="p-2 min-w-[96px]">
|
||||
<div class="h-4 w-full bg-neutral-300 rounded-sm overflow-hidden">
|
||||
<div
|
||||
class="h-4 w-full bg-neutral-300 rounded-sm overflow-hidden"
|
||||
>
|
||||
<div class="h-full bg-primary" :style="trace.value"></div>
|
||||
</div>
|
||||
</td>
|
||||
@@ -48,16 +82,34 @@
|
||||
<p class="h2 mb-2">Trace #{{ showTraceId }}</p>
|
||||
<div class="h-36 w-full px-2 mb-2 border border-neutral-300 rounded">
|
||||
<div class="h-full w-full">
|
||||
<div id="cyTrace" ref="cyTraceRef" class="h-full min-w-full relative"></div>
|
||||
<div
|
||||
id="cyTrace"
|
||||
ref="cyTraceRef"
|
||||
class="h-full min-w-full relative"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-y-auto overflow-x-auto scrollbar h-[calc(100%_-_200px)] infiniteTable" @scroll="handleScroll">
|
||||
<DataTable :value="caseData" showGridlines tableClass="text-sm" breakpoint="0">
|
||||
<div
|
||||
class="overflow-y-auto overflow-x-auto scrollbar h-[calc(100%_-_200px)] infiniteTable"
|
||||
@scroll="handleScroll"
|
||||
>
|
||||
<DataTable
|
||||
:value="caseData"
|
||||
showGridlines
|
||||
tableClass="text-sm"
|
||||
breakpoint="0"
|
||||
>
|
||||
<div v-for="(col, index) in columnData" :key="index">
|
||||
<Column :field="col.field" :header="col.header">
|
||||
<template #body="{ data }">
|
||||
<div :class="data[col.field]?.length > 18 ? 'whitespace-normal' : 'whitespace-nowrap'">
|
||||
{{ data[col.field] }}
|
||||
<div
|
||||
:class="
|
||||
data[col.field]?.length > 18
|
||||
? 'whitespace-normal'
|
||||
: 'whitespace-nowrap'
|
||||
"
|
||||
>
|
||||
{{ data[col.field] }}
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
@@ -65,7 +117,7 @@
|
||||
</DataTable>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -79,22 +131,28 @@
|
||||
* trace detail display.
|
||||
*/
|
||||
|
||||
import { ref, computed, watch, onMounted } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useAllMapDataStore } from '@/stores/allMapData';
|
||||
import { useLoadingStore } from '@/stores/loading';
|
||||
import cytoscapeMapTrace from '@/module/cytoscapeMapTrace.js';
|
||||
import { ref, computed, watch, onMounted } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useAllMapDataStore } from "@/stores/allMapData";
|
||||
import { useLoadingStore } from "@/stores/loading";
|
||||
import cytoscapeMapTrace from "@/module/cytoscapeMapTrace.js";
|
||||
|
||||
const emit = defineEmits(['filter-trace-selectArea']);
|
||||
const emit = defineEmits(["filter-trace-selectArea"]);
|
||||
|
||||
const allMapDataStore = useAllMapDataStore();
|
||||
const loadingStore = useLoadingStore();
|
||||
const { infinit404, baseInfiniteStart, baseTraces, baseTraceTaskSeq, baseCases } = storeToRefs(allMapDataStore);
|
||||
const {
|
||||
infinit404,
|
||||
baseInfiniteStart,
|
||||
baseTraces,
|
||||
baseTraceTaskSeq,
|
||||
baseCases,
|
||||
} = storeToRefs(allMapDataStore);
|
||||
const { isLoading } = storeToRefs(loadingStore);
|
||||
|
||||
const processMap = ref({
|
||||
nodes:[],
|
||||
edges:[],
|
||||
nodes: [],
|
||||
edges: [],
|
||||
});
|
||||
const showTraceId = ref(null);
|
||||
const infinitMaxItems = ref(false);
|
||||
@@ -111,95 +169,113 @@ const traceTotal = computed(() => {
|
||||
defineExpose({ selectArea, showTraceId, traceTotal });
|
||||
|
||||
const traceCountTotal = computed(() => {
|
||||
return baseTraces.value.map(trace => trace.count).reduce((acc, cur) => acc + cur, 0);
|
||||
return baseTraces.value
|
||||
.map((trace) => trace.count)
|
||||
.reduce((acc, cur) => acc + cur, 0);
|
||||
});
|
||||
|
||||
const traceList = computed(() => {
|
||||
return baseTraces.value.map(trace => {
|
||||
return {
|
||||
id: trace.id,
|
||||
value: progressWidth(Number(((trace.count / traceCountTotal.value) * 100).toFixed(1))),
|
||||
count: trace.count.toLocaleString(),
|
||||
base_count: trace.count,
|
||||
ratio: getPercentLabel(trace.count / traceCountTotal.value),
|
||||
};
|
||||
}).slice(selectArea.value[0], selectArea.value[1]);
|
||||
return baseTraces.value
|
||||
.map((trace) => {
|
||||
return {
|
||||
id: trace.id,
|
||||
value: progressWidth(
|
||||
Number(((trace.count / traceCountTotal.value) * 100).toFixed(1)),
|
||||
),
|
||||
count: trace.count.toLocaleString(),
|
||||
base_count: trace.count,
|
||||
ratio: getPercentLabel(trace.count / traceCountTotal.value),
|
||||
};
|
||||
})
|
||||
.slice(selectArea.value[0], selectArea.value[1]);
|
||||
});
|
||||
|
||||
const caseTotalPercent = computed(() => {
|
||||
const ratioSum = traceList.value.map(trace => trace.base_count).reduce((acc, cur) => acc + cur, 0) / traceCountTotal.value;
|
||||
return getPercentLabel(ratioSum)
|
||||
const ratioSum =
|
||||
traceList.value
|
||||
.map((trace) => trace.base_count)
|
||||
.reduce((acc, cur) => acc + cur, 0) / traceCountTotal.value;
|
||||
return getPercentLabel(ratioSum);
|
||||
});
|
||||
|
||||
const chartData = computed(() => {
|
||||
const start = selectArea.value[0];
|
||||
const end = selectArea.value[1] - 1;
|
||||
const labels = baseTraces.value.map(trace => `#${trace.id}`);
|
||||
const data = baseTraces.value.map(trace => getPercentLabel(trace.count / traceCountTotal.value));
|
||||
const selectAreaData = baseTraces.value.map((trace, index) => index >= start && index <= end ? 'rgba(0,153,255)' : 'rgba(203, 213, 225)');
|
||||
const labels = baseTraces.value.map((trace) => `#${trace.id}`);
|
||||
const data = baseTraces.value.map((trace) =>
|
||||
getPercentLabel(trace.count / traceCountTotal.value),
|
||||
);
|
||||
const selectAreaData = baseTraces.value.map((trace, index) =>
|
||||
index >= start && index <= end ? "rgba(0,153,255)" : "rgba(203, 213, 225)",
|
||||
);
|
||||
|
||||
return { // Data to display
|
||||
return {
|
||||
// Data to display
|
||||
labels,
|
||||
datasets: [
|
||||
{
|
||||
label: 'Trace', // Dataset label
|
||||
label: "Trace", // Dataset label
|
||||
data,
|
||||
backgroundColor: selectAreaData,
|
||||
categoryPercentage: 1.0,
|
||||
barPercentage: 1.0
|
||||
barPercentage: 1.0,
|
||||
},
|
||||
]
|
||||
],
|
||||
};
|
||||
});
|
||||
|
||||
const caseData = computed(() => {
|
||||
const data = JSON.parse(JSON.stringify(infiniteData.value)); // Deep copy the original cases data
|
||||
data.forEach(item => {
|
||||
data.forEach((item) => {
|
||||
item.attributes.forEach((attribute, index) => {
|
||||
item[`att_${index}`] = attribute.value; // Create a new key-value pair
|
||||
});
|
||||
delete item.attributes; // Remove the original attributes property
|
||||
})
|
||||
});
|
||||
return data;
|
||||
});
|
||||
|
||||
const columnData = computed(() => {
|
||||
const data = JSON.parse(JSON.stringify(baseCases.value)); // Deep copy the original cases data
|
||||
let result = [
|
||||
{ field: 'id', header: 'Case Id' },
|
||||
{ field: 'started_at', header: 'Start time' },
|
||||
{ field: 'completed_at', header: 'End time' },
|
||||
];
|
||||
if(data.length !== 0){
|
||||
{ field: "id", header: "Case Id" },
|
||||
{ field: "started_at", header: "Start time" },
|
||||
{ field: "completed_at", header: "End time" },
|
||||
];
|
||||
if (data.length !== 0) {
|
||||
result = [
|
||||
{ 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 })),
|
||||
{ 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,
|
||||
})),
|
||||
];
|
||||
}
|
||||
return result
|
||||
return result;
|
||||
});
|
||||
|
||||
watch(selectArea, (newValue, oldValue) => {
|
||||
const roundValue = Math.round(newValue[1].toFixed());
|
||||
if(newValue[1] !== roundValue) selectArea.value[1] = roundValue;
|
||||
if(newValue != oldValue) emit('filter-trace-selectArea', newValue); // Determine whether Apply should be disabled
|
||||
if (newValue[1] !== roundValue) selectArea.value[1] = roundValue;
|
||||
if (newValue != oldValue) emit("filter-trace-selectArea", newValue); // Determine whether Apply should be disabled
|
||||
});
|
||||
|
||||
watch(infinit404, (newValue) => {
|
||||
if(newValue === 404) infinitMaxItems.value = true;
|
||||
if (newValue === 404) infinitMaxItems.value = true;
|
||||
});
|
||||
|
||||
watch(showTraceId, (newValue, oldValue) => {
|
||||
const isScrollTop = document.querySelector('.infiniteTable');
|
||||
if(isScrollTop && typeof isScrollTop.scrollTop !== 'undefined') if(newValue !== oldValue) isScrollTop.scrollTop = 0;
|
||||
const isScrollTop = document.querySelector(".infiniteTable");
|
||||
if (isScrollTop && typeof isScrollTop.scrollTop !== "undefined")
|
||||
if (newValue !== oldValue) isScrollTop.scrollTop = 0;
|
||||
});
|
||||
|
||||
/**
|
||||
* Set bar chart Options
|
||||
*/
|
||||
function barOptions(){
|
||||
function barOptions() {
|
||||
return {
|
||||
maintainAspectRatio: false,
|
||||
aspectRatio: 0.8,
|
||||
@@ -208,41 +284,43 @@ function barOptions(){
|
||||
top: 16,
|
||||
left: 8,
|
||||
right: 8,
|
||||
}
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
legend: { // Legend
|
||||
legend: {
|
||||
// Legend
|
||||
display: false,
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: (tooltipItems) =>{
|
||||
return `${tooltipItems.dataset.label}: ${tooltipItems.parsed.y}%`
|
||||
}
|
||||
}
|
||||
}
|
||||
label: (tooltipItems) => {
|
||||
return `${tooltipItems.dataset.label}: ${tooltipItems.parsed.y}%`;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
animations: false,
|
||||
scales: {
|
||||
x: {
|
||||
display:false
|
||||
display: false,
|
||||
},
|
||||
y: {
|
||||
ticks: { // Set tick intervals
|
||||
ticks: {
|
||||
// Set tick intervals
|
||||
display: false, // Hide values, only show grid lines
|
||||
min: 0,
|
||||
max: traceList.value[0]?.ratio,
|
||||
stepSize: (traceList.value[0]?.ratio)/4,
|
||||
stepSize: traceList.value[0]?.ratio / 4,
|
||||
},
|
||||
grid: {
|
||||
color: 'rgba(100,116,139)',
|
||||
color: "rgba(100,116,139)",
|
||||
z: 1,
|
||||
},
|
||||
border: {
|
||||
display: false, // Hide the extra border line on the left
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -251,8 +329,8 @@ function barOptions(){
|
||||
* @param {number} val - The raw ratio value.
|
||||
* @returns {string} The formatted percentage string.
|
||||
*/
|
||||
function getPercentLabel(val){
|
||||
if((val * 100).toFixed(1) >= 100) return 100;
|
||||
function getPercentLabel(val) {
|
||||
if ((val * 100).toFixed(1) >= 100) return 100;
|
||||
else return parseFloat((val * 100).toFixed(1));
|
||||
}
|
||||
|
||||
@@ -261,8 +339,8 @@ function getPercentLabel(val){
|
||||
* @param {number} value - The percentage value.
|
||||
* @returns {string} The CSS width style string.
|
||||
*/
|
||||
function progressWidth(value){
|
||||
return `width:${value}%;`
|
||||
function progressWidth(value) {
|
||||
return `width:${value}%;`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -272,7 +350,7 @@ function progressWidth(value){
|
||||
*/
|
||||
async function switchCaseData(id, count) {
|
||||
// Do nothing if clicking the same id
|
||||
if(id == showTraceId.value) return;
|
||||
if (id == showTraceId.value) return;
|
||||
isLoading.value = true; // Always show loading screen
|
||||
infinit404.value = null;
|
||||
infinitMaxItems.value = false;
|
||||
@@ -287,7 +365,7 @@ async function switchCaseData(id, count) {
|
||||
/**
|
||||
* Assembles the trace element nodes data for Cytoscape rendering.
|
||||
*/
|
||||
function setNodesData(){
|
||||
function setNodesData() {
|
||||
// Clear nodes to prevent accumulation on each render
|
||||
processMap.value.nodes = [];
|
||||
// Populate nodes with data returned from the API call
|
||||
@@ -296,20 +374,20 @@ function setNodesData(){
|
||||
data: {
|
||||
id: index,
|
||||
label: node,
|
||||
backgroundColor: '#CCE5FF',
|
||||
bordercolor: '#003366',
|
||||
shape: 'round-rectangle',
|
||||
backgroundColor: "#CCE5FF",
|
||||
bordercolor: "#003366",
|
||||
shape: "round-rectangle",
|
||||
height: 80,
|
||||
width: 100
|
||||
}
|
||||
width: 100,
|
||||
},
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles the trace edge line data for Cytoscape rendering.
|
||||
*/
|
||||
function setEdgesData(){
|
||||
function setEdgesData() {
|
||||
processMap.value.edges = [];
|
||||
baseTraceTaskSeq.value.forEach((edge, index) => {
|
||||
processMap.value.edges.push({
|
||||
@@ -317,8 +395,8 @@ function setEdgesData(){
|
||||
source: `${index}`,
|
||||
target: `${index + 1}`,
|
||||
lineWidth: 1,
|
||||
style: 'solid'
|
||||
}
|
||||
style: "solid",
|
||||
},
|
||||
});
|
||||
});
|
||||
// The number of edges is one less than the number of nodes
|
||||
@@ -328,7 +406,7 @@ function setEdgesData(){
|
||||
/**
|
||||
* create trace cytoscape's map
|
||||
*/
|
||||
function createCy(){
|
||||
function createCy() {
|
||||
const graphId = cyTraceRef.value;
|
||||
|
||||
setNodesData();
|
||||
@@ -341,12 +419,18 @@ function createCy(){
|
||||
* @param {Event} event - The scroll event.
|
||||
*/
|
||||
function handleScroll(event) {
|
||||
if(infinitMaxItems.value || baseCases.value.length < 20 || infiniteFinish.value === false) return;
|
||||
if (
|
||||
infinitMaxItems.value ||
|
||||
baseCases.value.length < 20 ||
|
||||
infiniteFinish.value === false
|
||||
)
|
||||
return;
|
||||
|
||||
const container = event.target;
|
||||
const overScrollHeight = container.scrollTop + container.clientHeight >= container.scrollHeight;
|
||||
const overScrollHeight =
|
||||
container.scrollTop + container.clientHeight >= container.scrollHeight;
|
||||
|
||||
if(overScrollHeight) fetchData();
|
||||
if (overScrollHeight) fetchData();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -361,8 +445,8 @@ async function fetchData() {
|
||||
infiniteData.value = [...infiniteData.value, ...baseCases.value];
|
||||
infiniteFinish.value = true;
|
||||
isLoading.value = false;
|
||||
} catch(error) {
|
||||
console.error('Failed to load data:', error);
|
||||
} catch (error) {
|
||||
console.error("Failed to load data:", error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,7 +456,7 @@ onMounted(() => {
|
||||
setEdgesData();
|
||||
createCy();
|
||||
chartOptions.value = barOptions();
|
||||
selectArea.value = [0, traceTotal.value]
|
||||
selectArea.value = [0, traceTotal.value];
|
||||
isLoading.value = false;
|
||||
});
|
||||
</script>
|
||||
@@ -381,14 +465,14 @@ onMounted(() => {
|
||||
@reference "../../../../assets/tailwind.css";
|
||||
/* Table set */
|
||||
:deep(.p-datatable-thead) {
|
||||
@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) {
|
||||
@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
|
||||
@apply border-neutral-500 !border-t-0 text-center;
|
||||
}
|
||||
:deep(.p-datatable.p-datatable-gridlines .p-datatable-tbody > tr > td) {
|
||||
min-width: 72px;
|
||||
@@ -398,6 +482,6 @@ onMounted(() => {
|
||||
}
|
||||
/* Center datatable header */
|
||||
:deep(.p-column-header-content) {
|
||||
@apply justify-center
|
||||
@apply justify-center;
|
||||
}
|
||||
</style>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,13 @@
|
||||
<template>
|
||||
<Sidebar :visible="sidebarTraces" :closeIcon="'pi pi-chevron-left'" :modal="false" position="left" :dismissable="false" class="!w-11/12" @show="show()">
|
||||
<Sidebar
|
||||
:visible="sidebarTraces"
|
||||
:closeIcon="'pi pi-chevron-left'"
|
||||
:modal="false"
|
||||
position="left"
|
||||
:dismissable="false"
|
||||
class="!w-11/12"
|
||||
@show="show()"
|
||||
>
|
||||
<template #header>
|
||||
<p class="h1">Traces</p>
|
||||
</template>
|
||||
@@ -7,23 +15,37 @@
|
||||
<!-- Trace List -->
|
||||
<section class="w-80 h-full pr-4 border-r border-neutral-300">
|
||||
<p class="h2 px-2 mb-2">Trace List ({{ traceTotal }})</p>
|
||||
<p class="text-primary h2 px-2 mb-2">
|
||||
Click trace number to see more.
|
||||
</p>
|
||||
<div class="overflow-y-scroll overflow-x-hidden scrollbar mx-[-8px] max-h-[calc(100%_-_96px)]" >
|
||||
<p class="text-primary h2 px-2 mb-2">Click trace number to see more.</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>
|
||||
<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>
|
||||
<th class="h2 px-2 border-b border-neutral-500 text-start" colspan="3">Occurrences</th>
|
||||
<th
|
||||
class="h2 px-2 border-b border-neutral-500 text-start"
|
||||
colspan="3"
|
||||
>
|
||||
Occurrences
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(trace, key) in traceList" :key="key" class=" cursor-pointer hover:text-primary" @click="switchCaseData(trace.id, trace.base_count)">
|
||||
<tr
|
||||
v-for="(trace, key) in traceList"
|
||||
:key="key"
|
||||
class="cursor-pointer hover:text-primary"
|
||||
@click="switchCaseData(trace.id, trace.base_count)"
|
||||
>
|
||||
<td class="p-2">#{{ trace.id }}</td>
|
||||
<td class="p-2 w-24">
|
||||
<div class="h-4 w-full bg-neutral-300 rounded-sm overflow-hidden">
|
||||
<div
|
||||
class="h-4 w-full bg-neutral-300 rounded-sm overflow-hidden"
|
||||
>
|
||||
<div class="h-full bg-primary" :style="trace.value"></div>
|
||||
</div>
|
||||
</td>
|
||||
@@ -39,16 +61,34 @@
|
||||
<p class="h2 mb-2">Trace #{{ showTraceId }}</p>
|
||||
<div class="h-36 w-full px-2 mb-2 border border-neutral-300 rounded">
|
||||
<div class="h-full w-full">
|
||||
<div id="cyTrace" ref="cyTraceRef" class="h-full min-w-full relative"></div>
|
||||
<div
|
||||
id="cyTrace"
|
||||
ref="cyTraceRef"
|
||||
class="h-full min-w-full relative"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-y-auto overflow-x-auto scrollbar w-full h-[calc(100%_-_200px)] infiniteTable " @scroll="handleScroll">
|
||||
<DataTable :value="caseData" showGridlines tableClass="text-sm" breakpoint="0">
|
||||
<div
|
||||
class="overflow-y-auto overflow-x-auto scrollbar w-full h-[calc(100%_-_200px)] infiniteTable"
|
||||
@scroll="handleScroll"
|
||||
>
|
||||
<DataTable
|
||||
:value="caseData"
|
||||
showGridlines
|
||||
tableClass="text-sm"
|
||||
breakpoint="0"
|
||||
>
|
||||
<div v-for="(col, index) in columnData" :key="index">
|
||||
<Column :field="col.field" :header="col.header">
|
||||
<template #body="{ data }">
|
||||
<div :class="data[col.field]?.length > 18 ? 'whitespace-normal' : 'whitespace-nowrap'">
|
||||
{{ data[col.field] }}
|
||||
<div
|
||||
:class="
|
||||
data[col.field]?.length > 18
|
||||
? 'whitespace-normal'
|
||||
: 'whitespace-nowrap'
|
||||
"
|
||||
>
|
||||
{{ data[col.field] }}
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
@@ -70,23 +110,30 @@
|
||||
* clickable trace lists for highlighting on the map.
|
||||
*/
|
||||
|
||||
import { ref, computed, watch } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useLoadingStore } from '@/stores/loading';
|
||||
import { useAllMapDataStore } from '@/stores/allMapData';
|
||||
import cytoscapeMapTrace from '@/module/cytoscapeMapTrace.js';
|
||||
import { ref, computed, watch } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useLoadingStore } from "@/stores/loading";
|
||||
import { useAllMapDataStore } from "@/stores/allMapData";
|
||||
import cytoscapeMapTrace from "@/module/cytoscapeMapTrace.js";
|
||||
|
||||
const props = defineProps(['sidebarTraces', 'cases']);
|
||||
const emit = defineEmits(['switch-Trace-Id']);
|
||||
const props = defineProps(["sidebarTraces", "cases"]);
|
||||
const emit = defineEmits(["switch-Trace-Id"]);
|
||||
|
||||
const loadingStore = useLoadingStore();
|
||||
const allMapDataStore = useAllMapDataStore();
|
||||
const { isLoading } = storeToRefs(loadingStore);
|
||||
const { infinit404, infiniteStart, traceId, traces, traceTaskSeq, infiniteFirstCases } = storeToRefs(allMapDataStore);
|
||||
const {
|
||||
infinit404,
|
||||
infiniteStart,
|
||||
traceId,
|
||||
traces,
|
||||
traceTaskSeq,
|
||||
infiniteFirstCases,
|
||||
} = storeToRefs(allMapDataStore);
|
||||
|
||||
const processMap = ref({
|
||||
nodes:[],
|
||||
edges:[],
|
||||
nodes: [],
|
||||
edges: [],
|
||||
});
|
||||
const showTraceId = ref(null);
|
||||
const infinitMaxItems = ref(false);
|
||||
@@ -99,8 +146,10 @@ const traceTotal = computed(() => {
|
||||
});
|
||||
|
||||
const traceList = computed(() => {
|
||||
const sum = traces.value.map(trace => trace.count).reduce((acc, cur) => acc + cur, 0);
|
||||
const result = traces.value.map(trace => {
|
||||
const sum = traces.value
|
||||
.map((trace) => trace.count)
|
||||
.reduce((acc, cur) => acc + cur, 0);
|
||||
const result = traces.value.map((trace) => {
|
||||
return {
|
||||
id: trace.id,
|
||||
value: progressWidth(Number(((trace.count / sum) * 100).toFixed(1))),
|
||||
@@ -108,54 +157,63 @@ const traceList = computed(() => {
|
||||
base_count: trace.count,
|
||||
ratio: getPercentLabel(trace.count / sum),
|
||||
};
|
||||
})
|
||||
});
|
||||
return result;
|
||||
});
|
||||
|
||||
const caseData = computed(() => {
|
||||
const data = JSON.parse(JSON.stringify(infiniteData.value)); // Deep copy the original cases data
|
||||
data.forEach(item => {
|
||||
data.forEach((item) => {
|
||||
item.attributes.forEach((attribute, index) => {
|
||||
item[`att_${index}`] = attribute.value; // Create a new key-value pair
|
||||
});
|
||||
delete item.attributes; // Remove the original attributes property
|
||||
})
|
||||
});
|
||||
return data;
|
||||
});
|
||||
|
||||
const columnData = computed(() => {
|
||||
const data = JSON.parse(JSON.stringify(props.cases)); // Deep copy the original cases data
|
||||
let result = [
|
||||
{ field: 'id', header: 'Case Id' },
|
||||
{ field: 'started_at', header: 'Start time' },
|
||||
{ field: 'completed_at', header: 'End time' },
|
||||
];
|
||||
if(data.length !== 0){
|
||||
{ field: "id", header: "Case Id" },
|
||||
{ field: "started_at", header: "Start time" },
|
||||
{ field: "completed_at", header: "End time" },
|
||||
];
|
||||
if (data.length !== 0) {
|
||||
result = [
|
||||
{ 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 })),
|
||||
{ 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,
|
||||
})),
|
||||
];
|
||||
}
|
||||
return result
|
||||
return result;
|
||||
});
|
||||
|
||||
watch(infinit404, (newValue) => {
|
||||
if(newValue === 404) infinitMaxItems.value = true;
|
||||
if (newValue === 404) infinitMaxItems.value = true;
|
||||
});
|
||||
|
||||
watch(traceId, (newValue) => {
|
||||
showTraceId.value = newValue;
|
||||
}, { immediate: true });
|
||||
watch(
|
||||
traceId,
|
||||
(newValue) => {
|
||||
showTraceId.value = newValue;
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
watch(showTraceId, (newValue, oldValue) => {
|
||||
const isScrollTop = document.querySelector('.infiniteTable');
|
||||
if(isScrollTop && typeof isScrollTop.scrollTop !== 'undefined') if(newValue !== oldValue) isScrollTop.scrollTop = 0;
|
||||
const isScrollTop = document.querySelector(".infiniteTable");
|
||||
if (isScrollTop && typeof isScrollTop.scrollTop !== "undefined")
|
||||
if (newValue !== oldValue) isScrollTop.scrollTop = 0;
|
||||
});
|
||||
|
||||
watch(infiniteFirstCases, (newValue) => {
|
||||
if(infiniteFirstCases.value) infiniteData.value = JSON.parse(JSON.stringify(newValue));
|
||||
if (infiniteFirstCases.value)
|
||||
infiniteData.value = JSON.parse(JSON.stringify(newValue));
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -163,8 +221,8 @@ watch(infiniteFirstCases, (newValue) => {
|
||||
* @param {number} val - The raw ratio value.
|
||||
* @returns {string} The formatted percentage string.
|
||||
*/
|
||||
function getPercentLabel(val){
|
||||
if((val * 100).toFixed(1) >= 100) return `100%`;
|
||||
function getPercentLabel(val) {
|
||||
if ((val * 100).toFixed(1) >= 100) return `100%`;
|
||||
else return `${(val * 100).toFixed(1)}%`;
|
||||
}
|
||||
|
||||
@@ -173,8 +231,8 @@ function getPercentLabel(val){
|
||||
* @param {number} value - The percentage value.
|
||||
* @returns {string} The CSS width style string.
|
||||
*/
|
||||
function progressWidth(value){
|
||||
return `width:${value}%;`
|
||||
function progressWidth(value) {
|
||||
return `width:${value}%;`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -184,19 +242,19 @@ function progressWidth(value){
|
||||
*/
|
||||
async function switchCaseData(id, count) {
|
||||
// Do nothing if clicking the same id
|
||||
if(id == showTraceId.value) return;
|
||||
if (id == showTraceId.value) return;
|
||||
isLoading.value = true; // Always show loading screen
|
||||
infinit404.value = null;
|
||||
infinitMaxItems.value = false;
|
||||
showTraceId.value = id;
|
||||
infiniteStart.value = 0;
|
||||
emit('switch-Trace-Id', {id: showTraceId.value, count: count}); // Pass to Map index, which will close loading
|
||||
emit("switch-Trace-Id", { id: showTraceId.value, count: count }); // Pass to Map index, which will close loading
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles the trace element nodes data for Cytoscape rendering.
|
||||
*/
|
||||
function setNodesData(){
|
||||
function setNodesData() {
|
||||
// Clear nodes to prevent accumulation on each render
|
||||
processMap.value.nodes = [];
|
||||
// Populate nodes with data returned from the API call
|
||||
@@ -205,20 +263,20 @@ function setNodesData(){
|
||||
data: {
|
||||
id: index,
|
||||
label: node,
|
||||
backgroundColor: '#CCE5FF',
|
||||
bordercolor: '#003366',
|
||||
shape: 'round-rectangle',
|
||||
backgroundColor: "#CCE5FF",
|
||||
bordercolor: "#003366",
|
||||
shape: "round-rectangle",
|
||||
height: 80,
|
||||
width: 100
|
||||
}
|
||||
width: 100,
|
||||
},
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles the trace edge line data for Cytoscape rendering.
|
||||
*/
|
||||
function setEdgesData(){
|
||||
function setEdgesData() {
|
||||
processMap.value.edges = [];
|
||||
traceTaskSeq.value.forEach((edge, index) => {
|
||||
processMap.value.edges.push({
|
||||
@@ -226,8 +284,8 @@ function setEdgesData(){
|
||||
source: `${index}`,
|
||||
target: `${index + 1}`,
|
||||
lineWidth: 1,
|
||||
style: 'solid'
|
||||
}
|
||||
style: "solid",
|
||||
},
|
||||
});
|
||||
});
|
||||
// The number of edges is one less than the number of nodes
|
||||
@@ -237,7 +295,7 @@ function setEdgesData(){
|
||||
/**
|
||||
* create trace cytoscape's map
|
||||
*/
|
||||
function createCy(){
|
||||
function createCy() {
|
||||
const graphId = cyTraceRef.value;
|
||||
|
||||
setNodesData();
|
||||
@@ -264,12 +322,18 @@ async function show() {
|
||||
* @param {Event} event - The scroll event.
|
||||
*/
|
||||
function handleScroll(event) {
|
||||
if(infinitMaxItems.value || props.cases.length < 20 || infiniteFinish.value === false) return;
|
||||
if (
|
||||
infinitMaxItems.value ||
|
||||
props.cases.length < 20 ||
|
||||
infiniteFinish.value === false
|
||||
)
|
||||
return;
|
||||
|
||||
const container = event.target;
|
||||
const overScrollHeight = container.scrollTop + container.clientHeight >= container.scrollHeight;
|
||||
const overScrollHeight =
|
||||
container.scrollTop + container.clientHeight >= container.scrollHeight;
|
||||
|
||||
if(overScrollHeight) fetchData();
|
||||
if (overScrollHeight) fetchData();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -284,8 +348,8 @@ async function fetchData() {
|
||||
infiniteData.value = [...infiniteData.value, ...props.cases];
|
||||
infiniteFinish.value = true;
|
||||
isLoading.value = false;
|
||||
} catch(error) {
|
||||
console.error('Failed to load data:', error);
|
||||
} catch (error) {
|
||||
console.error("Failed to load data:", error);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -294,18 +358,18 @@ async function fetchData() {
|
||||
@reference "../../../assets/tailwind.css";
|
||||
/* Progress bar color */
|
||||
:deep(.p-progressbar .p-progressbar-value) {
|
||||
@apply bg-primary
|
||||
@apply bg-primary;
|
||||
}
|
||||
/* Table set */
|
||||
:deep(.p-datatable-thead) {
|
||||
@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) {
|
||||
@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
|
||||
@apply border-neutral-500 !border-t-0 text-center;
|
||||
}
|
||||
:deep(.p-datatable.p-datatable-gridlines .p-datatable-tbody > tr > td) {
|
||||
min-width: 72px;
|
||||
@@ -315,6 +379,6 @@ async function fetchData() {
|
||||
}
|
||||
/* Center datatable header */
|
||||
:deep(.p-column-header-content) {
|
||||
@apply justify-center
|
||||
@apply justify-center;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
<template>
|
||||
<Sidebar :visible="sidebarView" :closeIcon="'pi pi-chevron-left'" :modal="false" position="left" :dismissable="false" >
|
||||
<Sidebar
|
||||
:visible="sidebarView"
|
||||
:closeIcon="'pi pi-chevron-left'"
|
||||
:modal="false"
|
||||
position="left"
|
||||
:dismissable="false"
|
||||
>
|
||||
<template #header>
|
||||
<p class="h1">Visualization Setting</p>
|
||||
</template>
|
||||
@@ -10,28 +16,54 @@
|
||||
<ul class="space-y-3 mb-4">
|
||||
<!-- Select bpmn / processmap button -->
|
||||
<li class="btn-toggle-content">
|
||||
<span class="btn-toggle-item" :class="mapType === 'processMap'?'btn-toggle-show ':''" @click="onProcessMapClick()">
|
||||
<span
|
||||
class="btn-toggle-item"
|
||||
:class="mapType === 'processMap' ? 'btn-toggle-show ' : ''"
|
||||
@click="onProcessMapClick()"
|
||||
>
|
||||
Process Map
|
||||
</span>
|
||||
<span class="btn-toggle-item" :class="mapType === 'bpmn'?'btn-toggle-show':''" @click="onBPMNClick()">
|
||||
<span
|
||||
class="btn-toggle-item"
|
||||
:class="mapType === 'bpmn' ? 'btn-toggle-show' : ''"
|
||||
@click="onBPMNClick()"
|
||||
>
|
||||
BPMN Model
|
||||
</span>
|
||||
</li>
|
||||
<!-- Select drawing style: bezier / unbundled-bezier button -->
|
||||
<li class="btn-toggle-content">
|
||||
<span class="btn-toggle-item" :class="curveStyle === 'unbundled-bezier'?'btn-toggle-show ':''" @click="switchCurveStyles('unbundled-bezier')">
|
||||
<span
|
||||
class="btn-toggle-item"
|
||||
:class="
|
||||
curveStyle === 'unbundled-bezier' ? 'btn-toggle-show ' : ''
|
||||
"
|
||||
@click="switchCurveStyles('unbundled-bezier')"
|
||||
>
|
||||
Curved
|
||||
</span>
|
||||
<span class="btn-toggle-item" :class="curveStyle === 'taxi'?'btn-toggle-show':''" @click="switchCurveStyles('taxi')">
|
||||
<span
|
||||
class="btn-toggle-item"
|
||||
:class="curveStyle === 'taxi' ? 'btn-toggle-show' : ''"
|
||||
@click="switchCurveStyles('taxi')"
|
||||
>
|
||||
Elbow
|
||||
</span>
|
||||
</li>
|
||||
<!-- Vertical TB | Horizontal LR -->
|
||||
<li class="btn-toggle-content">
|
||||
<span class="btn-toggle-item" :class="rank === 'LR'?'btn-toggle-show ':''" @click="switchRank('LR')">
|
||||
<span
|
||||
class="btn-toggle-item"
|
||||
:class="rank === 'LR' ? 'btn-toggle-show ' : ''"
|
||||
@click="switchRank('LR')"
|
||||
>
|
||||
Horizontal
|
||||
</span>
|
||||
<span class="btn-toggle-item" :class="rank === 'TB'?'btn-toggle-show':''" @click="switchRank('TB')">
|
||||
<span
|
||||
class="btn-toggle-item"
|
||||
:class="rank === 'TB' ? 'btn-toggle-show' : ''"
|
||||
@click="switchRank('TB')"
|
||||
>
|
||||
Vertical
|
||||
</span>
|
||||
</li>
|
||||
@@ -41,25 +73,67 @@
|
||||
<div>
|
||||
<p class="h2">Data Layer</p>
|
||||
<ul class="space-y-2">
|
||||
<li class="flex justify-between mb-3" @change="switchDataLayerType($event, 'freq')">
|
||||
<li
|
||||
class="flex justify-between mb-3"
|
||||
@change="switchDataLayerType($event, 'freq')"
|
||||
>
|
||||
<div class="flex items-center w-1/2">
|
||||
<RadioButton v-model="dataLayerType" inputId="freq" name="dataLayer" value="freq" class="mr-2" @click.prevent="switchDataLayerType($event, 'freq')"/>
|
||||
<RadioButton
|
||||
v-model="dataLayerType"
|
||||
inputId="freq"
|
||||
name="dataLayer"
|
||||
value="freq"
|
||||
class="mr-2"
|
||||
@click.prevent="switchDataLayerType($event, 'freq')"
|
||||
/>
|
||||
<label for="freq">Frequency</label>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
<select class="border border-neutral-500 rounded p-1 w-full focus-visible:outline-primary" :disabled="dataLayerType === 'duration'">
|
||||
<option v-for="(freq, index) in selectFrequency" :key="index" :value="freq.value" :disabled="freq.disabled" :selected="freq.value === selectedFreq">{{ freq.label }}</option>
|
||||
<select
|
||||
class="border border-neutral-500 rounded p-1 w-full focus-visible:outline-primary"
|
||||
:disabled="dataLayerType === 'duration'"
|
||||
>
|
||||
<option
|
||||
v-for="(freq, index) in selectFrequency"
|
||||
:key="index"
|
||||
:value="freq.value"
|
||||
:disabled="freq.disabled"
|
||||
:selected="freq.value === selectedFreq"
|
||||
>
|
||||
{{ freq.label }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex justify-between mb-3" @change="switchDataLayerType($event, 'duration')">
|
||||
<li
|
||||
class="flex justify-between mb-3"
|
||||
@change="switchDataLayerType($event, 'duration')"
|
||||
>
|
||||
<div class="flex items-center w-1/2">
|
||||
<RadioButton v-model="dataLayerType" inputId="duration" name="dataLayer" value="duration" class="mr-2" @click.prevent="switchDataLayerType($event, 'duration')"/>
|
||||
<RadioButton
|
||||
v-model="dataLayerType"
|
||||
inputId="duration"
|
||||
name="dataLayer"
|
||||
value="duration"
|
||||
class="mr-2"
|
||||
@click.prevent="switchDataLayerType($event, 'duration')"
|
||||
/>
|
||||
<label for="duration">Duration</label>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
<select class="border border-neutral-500 rounded p-1 w-full focus-visible:outline-primary" :disabled="dataLayerType === 'freq'">
|
||||
<option v-for="(duration, index) in selectDuration" :key="index" :value="duration.value" :disabled="duration.disabled" :selected="duration.value === selectedDuration">{{ duration.label }}</option>
|
||||
<select
|
||||
class="border border-neutral-500 rounded p-1 w-full focus-visible:outline-primary"
|
||||
:disabled="dataLayerType === 'freq'"
|
||||
>
|
||||
<option
|
||||
v-for="(duration, index) in selectDuration"
|
||||
:key="index"
|
||||
:value="duration.value"
|
||||
:disabled="duration.disabled"
|
||||
:selected="duration.value === selectedDuration"
|
||||
>
|
||||
{{ duration.label }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</li>
|
||||
@@ -80,9 +154,9 @@
|
||||
* style, direction, and data layer selection.
|
||||
*/
|
||||
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useMapPathStore } from '@/stores/mapPathStore';
|
||||
import { ref, onMounted } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useMapPathStore } from "@/stores/mapPathStore";
|
||||
|
||||
defineProps({
|
||||
sidebarView: {
|
||||
@@ -92,39 +166,39 @@ defineProps({
|
||||
});
|
||||
|
||||
const emit = defineEmits([
|
||||
'switch-map-type',
|
||||
'switch-curve-styles',
|
||||
'switch-rank',
|
||||
'switch-data-layer-type',
|
||||
"switch-map-type",
|
||||
"switch-curve-styles",
|
||||
"switch-rank",
|
||||
"switch-data-layer-type",
|
||||
]);
|
||||
|
||||
const mapPathStore = useMapPathStore();
|
||||
const { isBPMNOn } = storeToRefs(mapPathStore);
|
||||
|
||||
const selectFrequency = ref([
|
||||
{ value:"total", label:"Total", disabled:false, },
|
||||
{ value:"rel_freq", label:"Relative", disabled:false, },
|
||||
{ value:"average", label:"Average", disabled:false, },
|
||||
{ value:"median", label:"Median", disabled:false, },
|
||||
{ value:"max", label:"Max", disabled:false, },
|
||||
{ value:"min", label:"Min", disabled:false, },
|
||||
{ value:"cases", label:"Number of cases", disabled:false, },
|
||||
{ value: "total", label: "Total", disabled: false },
|
||||
{ value: "rel_freq", label: "Relative", disabled: false },
|
||||
{ value: "average", label: "Average", disabled: false },
|
||||
{ value: "median", label: "Median", disabled: false },
|
||||
{ value: "max", label: "Max", disabled: false },
|
||||
{ value: "min", label: "Min", disabled: false },
|
||||
{ value: "cases", label: "Number of cases", disabled: false },
|
||||
]);
|
||||
const selectDuration = ref([
|
||||
{ value:"total", label:"Total", disabled:false, },
|
||||
{ value:"rel_duration", label:"Relative", disabled:false, },
|
||||
{ value:"average", label:"Average", disabled:false, },
|
||||
{ value:"median", label:"Median", disabled:false, },
|
||||
{ value:"max", label:"Max", disabled:false, },
|
||||
{ value:"min", label:"Min", disabled:false, },
|
||||
{ value: "total", label: "Total", disabled: false },
|
||||
{ value: "rel_duration", label: "Relative", disabled: false },
|
||||
{ value: "average", label: "Average", disabled: false },
|
||||
{ value: "median", label: "Median", disabled: false },
|
||||
{ value: "max", label: "Max", disabled: false },
|
||||
{ value: "min", label: "Min", disabled: false },
|
||||
]);
|
||||
const curveStyle = ref('unbundled-bezier'); // unbundled-bezier | taxi
|
||||
const mapType = ref('processMap'); // processMap | bpmn
|
||||
const curveStyle = ref("unbundled-bezier"); // unbundled-bezier | taxi
|
||||
const mapType = ref("processMap"); // processMap | bpmn
|
||||
const dataLayerType = ref(null); // freq | duration
|
||||
const dataLayerOption = ref(null);
|
||||
const selectedFreq = ref('');
|
||||
const selectedDuration = ref('');
|
||||
const rank = ref('LR'); // Vertical TB | Horizontal LR
|
||||
const selectedFreq = ref("");
|
||||
const selectedDuration = ref("");
|
||||
const rank = ref("LR"); // Vertical TB | Horizontal LR
|
||||
|
||||
/**
|
||||
* Switches the map type and emits the change event.
|
||||
@@ -132,7 +206,7 @@ const rank = ref('LR'); // Vertical TB | Horizontal LR
|
||||
*/
|
||||
function switchMapType(type) {
|
||||
mapType.value = type;
|
||||
emit('switch-map-type', mapType.value);
|
||||
emit("switch-map-type", mapType.value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,7 +215,7 @@ function switchMapType(type) {
|
||||
*/
|
||||
function switchCurveStyles(style) {
|
||||
curveStyle.value = style;
|
||||
emit('switch-curve-styles', curveStyle.value);
|
||||
emit("switch-curve-styles", curveStyle.value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,7 +224,7 @@ function switchCurveStyles(style) {
|
||||
*/
|
||||
function switchRank(rankValue) {
|
||||
rank.value = rankValue;
|
||||
emit('switch-rank', rank.value);
|
||||
emit("switch-rank", rank.value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -159,40 +233,41 @@ function switchRank(rankValue) {
|
||||
* @param {string} type - 'freq' or 'duration'.
|
||||
*/
|
||||
function switchDataLayerType(e, type) {
|
||||
let value = '';
|
||||
let value = "";
|
||||
|
||||
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) {
|
||||
case 'freq':
|
||||
value = value || selectedFreq.value || 'total';
|
||||
case "freq":
|
||||
value = value || selectedFreq.value || "total";
|
||||
dataLayerType.value = type;
|
||||
dataLayerOption.value = value;
|
||||
selectedFreq.value = value;
|
||||
break;
|
||||
case 'duration':
|
||||
value = value || selectedDuration.value || 'total';
|
||||
case "duration":
|
||||
value = value || selectedDuration.value || "total";
|
||||
dataLayerType.value = type;
|
||||
dataLayerOption.value = value;
|
||||
selectedDuration.value = value;
|
||||
break;
|
||||
}
|
||||
emit('switch-data-layer-type', dataLayerType.value, dataLayerOption.value);
|
||||
emit("switch-data-layer-type", dataLayerType.value, dataLayerOption.value);
|
||||
}
|
||||
|
||||
/** Switches to Process Map view. */
|
||||
function onProcessMapClick() {
|
||||
mapPathStore.setIsBPMNOn(false);
|
||||
switchMapType('processMap');
|
||||
switchMapType("processMap");
|
||||
}
|
||||
|
||||
/** Switches to BPMN Model view. */
|
||||
function onBPMNClick() {
|
||||
mapPathStore.setIsBPMNOn(true);
|
||||
switchMapType('bpmn');
|
||||
switchMapType("bpmn");
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
dataLayerType.value = 'freq';
|
||||
dataLayerOption.value = 'total';
|
||||
dataLayerType.value = "freq";
|
||||
dataLayerOption.value = "total";
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,81 +1,149 @@
|
||||
<template>
|
||||
<section class="w-full top-0 absolute shadow-[0px_6px_6px_inset_rgba(0,0,0,0.1)] z-20">
|
||||
<!-- status content -->
|
||||
<ul class="bg-neutral-100 flex justify-start shadow-[0px_1px_4px_rgba(0,0,0,0.2)] gap-3 p-3 text-sm overflow-x-auto scrollbar duration-700" v-show="isPanel" v-if="statData">
|
||||
<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>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="mr-2 w-full">
|
||||
<span class="block text-sm mb-2">{{ statData.cases.count }} / {{ statData.cases.total }}</span>
|
||||
<ProgressBar :value="statData.cases.ratio" :showValue="false" class="!h-1.5 min-w-[136px] rounded !bg-neutral-200"></ProgressBar>
|
||||
<section
|
||||
class="w-full top-0 absolute shadow-[0px_6px_6px_inset_rgba(0,0,0,0.1)] z-20"
|
||||
>
|
||||
<!-- status content -->
|
||||
<ul
|
||||
class="bg-neutral-100 flex justify-start shadow-[0px_1px_4px_rgba(0,0,0,0.2)] gap-3 p-3 text-sm overflow-x-auto scrollbar duration-700"
|
||||
v-show="isPanel"
|
||||
v-if="statData"
|
||||
>
|
||||
<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>
|
||||
</div>
|
||||
<span class="block text-2xl font-medium">{{ statData.cases.ratio }}%</span>
|
||||
</div>
|
||||
</li>
|
||||
<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>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="mr-2 w-full">
|
||||
<span class="block text-sm mb-2">{{ statData.traces.count }} / {{ statData.traces.total }}</span>
|
||||
<ProgressBar :value="statData.traces.ratio" :showValue="false" class="!h-1.5 min-w-[136px] rounded !bg-neutral-200"></ProgressBar>
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="mr-2 w-full">
|
||||
<span class="block text-sm mb-2"
|
||||
>{{ statData.cases.count }} / {{ statData.cases.total }}</span
|
||||
>
|
||||
<ProgressBar
|
||||
:value="statData.cases.ratio"
|
||||
:showValue="false"
|
||||
class="!h-1.5 min-w-[136px] rounded !bg-neutral-200"
|
||||
></ProgressBar>
|
||||
</div>
|
||||
<span class="block text-2xl font-medium"
|
||||
>{{ statData.cases.ratio }}%</span
|
||||
>
|
||||
</div>
|
||||
<span class="block text-2xl font-medium">{{ statData.traces.ratio }}%</span>
|
||||
</div>
|
||||
</li>
|
||||
<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>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="mr-2 w-full">
|
||||
<span class="block text-sm mb-2">{{ statData.task_instances.count }} / {{ statData.task_instances.total }}</span>
|
||||
<ProgressBar :value="statData.task_instances.ratio" :showValue="false" class="!h-1.5 min-w-[136px] rounded !bg-neutral-200"></ProgressBar>
|
||||
</li>
|
||||
<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>
|
||||
</div>
|
||||
<span class="block text-2xl font-medium">{{ statData.task_instances.ratio }}%</span>
|
||||
</div>
|
||||
</li>
|
||||
<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>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="mr-2 w-full">
|
||||
<span class="block text-sm mb-2">{{ statData.tasks.count }} / {{ statData.tasks.total }}</span>
|
||||
<ProgressBar :value="statData.tasks.ratio" :showValue="false" class="!h-1.5 min-w-[136px] rounded !bg-neutral-200"></ProgressBar>
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="mr-2 w-full">
|
||||
<span class="block text-sm mb-2"
|
||||
>{{ statData.traces.count }} / {{ statData.traces.total }}</span
|
||||
>
|
||||
<ProgressBar
|
||||
:value="statData.traces.ratio"
|
||||
:showValue="false"
|
||||
class="!h-1.5 min-w-[136px] rounded !bg-neutral-200"
|
||||
></ProgressBar>
|
||||
</div>
|
||||
<span class="block text-2xl font-medium"
|
||||
>{{ statData.traces.ratio }}%</span
|
||||
>
|
||||
</div>
|
||||
<span class="block text-2xl font-medium">{{ statData.tasks.ratio }}%</span>
|
||||
</div>
|
||||
</li>
|
||||
<li class="bg-neutral-10 rounded p-3 w-full">
|
||||
<p class="font-bold text-sm leading-8 mb-2.5">Log Timeframe</p>
|
||||
<div class="px-2 space-y-2 min-w-[140px] h-[40px]">
|
||||
<span class="inline-block">{{ statData.started_at }} </span>
|
||||
<span class="inline-block">~ {{ statData.completed_at }}</span>
|
||||
</div>
|
||||
</li>
|
||||
<li class="bg-neutral-10 rounded p-3 w-full">
|
||||
<p class="font-bold text-sm leading-8">Case Duration</p>
|
||||
<div class="flex justify-between items-center space-x-2 min-w-[272px]">
|
||||
<div class="space-y-2">
|
||||
<p><Tag value="MAX" class="!text-neutral-900 !bg-neutral-300 mr-2 !w-10 !text-sm !px-2 !py-0"></Tag>{{ statData.case_duration.max }}</p>
|
||||
<p><Tag value="MIN" class="!text-neutral-900 !bg-neutral-300 mr-2 !w-10 !text-sm !px-2 !py-0"></Tag>{{ statData.case_duration.min }}</p>
|
||||
</li>
|
||||
<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>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<p><Tag value="MED" class="!text-neutral-900 !bg-neutral-300 mr-2 !w-10 !text-sm !px-2 !py-0"></Tag>{{ statData.case_duration.median }}</p>
|
||||
<p><Tag value="AVG" class="!text-neutral-900 !bg-neutral-300 mr-2 !w-10 !text-sm !px-2 !py-0"></Tag>{{ statData.case_duration.average }}</p>
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="mr-2 w-full">
|
||||
<span class="block text-sm mb-2"
|
||||
>{{ statData.task_instances.count }} /
|
||||
{{ statData.task_instances.total }}</span
|
||||
>
|
||||
<ProgressBar
|
||||
:value="statData.task_instances.ratio"
|
||||
:showValue="false"
|
||||
class="!h-1.5 min-w-[136px] rounded !bg-neutral-200"
|
||||
></ProgressBar>
|
||||
</div>
|
||||
<span class="block text-2xl font-medium"
|
||||
>{{ statData.task_instances.ratio }}%</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- control button -->
|
||||
<div class="bg-neutral-300 rounded-b-full w-20 text-center mx-auto cursor-pointer hover:bg-neutral-500 hover:text-neutral-10 active:ring focus:outline-none focus:border-neutral-500 focus:ring" @click="isPanel = !isPanel">
|
||||
<span class="material-symbols-outlined block px-8 !text-xs ">{{ isPanel ? 'keyboard_double_arrow_up' : 'keyboard_double_arrow_down' }}</span>
|
||||
</div>
|
||||
</section>
|
||||
</li>
|
||||
<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>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="mr-2 w-full">
|
||||
<span class="block text-sm mb-2"
|
||||
>{{ statData.tasks.count }} / {{ statData.tasks.total }}</span
|
||||
>
|
||||
<ProgressBar
|
||||
:value="statData.tasks.ratio"
|
||||
:showValue="false"
|
||||
class="!h-1.5 min-w-[136px] rounded !bg-neutral-200"
|
||||
></ProgressBar>
|
||||
</div>
|
||||
<span class="block text-2xl font-medium"
|
||||
>{{ statData.tasks.ratio }}%</span
|
||||
>
|
||||
</div>
|
||||
</li>
|
||||
<li class="bg-neutral-10 rounded p-3 w-full">
|
||||
<p class="font-bold text-sm leading-8 mb-2.5">Log Timeframe</p>
|
||||
<div class="px-2 space-y-2 min-w-[140px] h-[40px]">
|
||||
<span class="inline-block">{{ statData.started_at }} </span>
|
||||
<span class="inline-block">~ {{ statData.completed_at }}</span>
|
||||
</div>
|
||||
</li>
|
||||
<li class="bg-neutral-10 rounded p-3 w-full">
|
||||
<p class="font-bold text-sm leading-8">Case Duration</p>
|
||||
<div class="flex justify-between items-center space-x-2 min-w-[272px]">
|
||||
<div class="space-y-2">
|
||||
<p>
|
||||
<Tag
|
||||
value="MAX"
|
||||
class="!text-neutral-900 !bg-neutral-300 mr-2 !w-10 !text-sm !px-2 !py-0"
|
||||
></Tag
|
||||
>{{ statData.case_duration.max }}
|
||||
</p>
|
||||
<p>
|
||||
<Tag
|
||||
value="MIN"
|
||||
class="!text-neutral-900 !bg-neutral-300 mr-2 !w-10 !text-sm !px-2 !py-0"
|
||||
></Tag
|
||||
>{{ statData.case_duration.min }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<p>
|
||||
<Tag
|
||||
value="MED"
|
||||
class="!text-neutral-900 !bg-neutral-300 mr-2 !w-10 !text-sm !px-2 !py-0"
|
||||
></Tag
|
||||
>{{ statData.case_duration.median }}
|
||||
</p>
|
||||
<p>
|
||||
<Tag
|
||||
value="AVG"
|
||||
class="!text-neutral-900 !bg-neutral-300 mr-2 !w-10 !text-sm !px-2 !py-0"
|
||||
></Tag
|
||||
>{{ statData.case_duration.average }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- control button -->
|
||||
<div
|
||||
class="bg-neutral-300 rounded-b-full w-20 text-center mx-auto cursor-pointer hover:bg-neutral-500 hover:text-neutral-10 active:ring focus:outline-none focus:border-neutral-500 focus:ring"
|
||||
@click="isPanel = !isPanel"
|
||||
>
|
||||
<span class="material-symbols-outlined block px-8 !text-xs">{{
|
||||
isPanel ? "keyboard_double_arrow_up" : "keyboard_double_arrow_down"
|
||||
}}</span>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -89,12 +157,12 @@
|
||||
* timeframe, case duration) for the Discover page.
|
||||
*/
|
||||
|
||||
import { ref, onMounted, } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useAllMapDataStore } from '@/stores/allMapData';
|
||||
import { getTimeLabel } from '@/module/timeLabel.js';
|
||||
import getMoment from 'moment';
|
||||
import { ref, onMounted } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useAllMapDataStore } from "@/stores/allMapData";
|
||||
import { getTimeLabel } from "@/module/timeLabel.js";
|
||||
import getMoment from "moment";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
@@ -109,8 +177,8 @@ const statData = ref(null);
|
||||
* @param {number} val - The ratio value to convert.
|
||||
* @returns {number} The percentage value.
|
||||
*/
|
||||
function getPercentLabel(val){
|
||||
if((val * 100).toFixed(1) >= 100) return 100;
|
||||
function getPercentLabel(val) {
|
||||
if ((val * 100).toFixed(1) >= 100) return 100;
|
||||
else return parseFloat((val * 100).toFixed(1));
|
||||
}
|
||||
|
||||
@@ -118,46 +186,48 @@ function getPercentLabel(val){
|
||||
function getStatData() {
|
||||
statData.value = {
|
||||
cases: {
|
||||
count: stats.value.cases.count.toLocaleString('en-US'),
|
||||
total: stats.value.cases.total.toLocaleString('en-US'),
|
||||
ratio: getPercentLabel(stats.value.cases.ratio)
|
||||
count: stats.value.cases.count.toLocaleString("en-US"),
|
||||
total: stats.value.cases.total.toLocaleString("en-US"),
|
||||
ratio: getPercentLabel(stats.value.cases.ratio),
|
||||
},
|
||||
traces: {
|
||||
count: stats.value.traces.count.toLocaleString('en-US'),
|
||||
total: stats.value.traces.total.toLocaleString('en-US'),
|
||||
ratio: getPercentLabel(stats.value.traces.ratio)
|
||||
count: stats.value.traces.count.toLocaleString("en-US"),
|
||||
total: stats.value.traces.total.toLocaleString("en-US"),
|
||||
ratio: getPercentLabel(stats.value.traces.ratio),
|
||||
},
|
||||
task_instances: {
|
||||
count: stats.value.task_instances.count.toLocaleString('en-US'),
|
||||
total: stats.value.task_instances.total.toLocaleString('en-US'),
|
||||
ratio: getPercentLabel(stats.value.task_instances.ratio)
|
||||
count: stats.value.task_instances.count.toLocaleString("en-US"),
|
||||
total: stats.value.task_instances.total.toLocaleString("en-US"),
|
||||
ratio: getPercentLabel(stats.value.task_instances.ratio),
|
||||
},
|
||||
tasks: {
|
||||
count: stats.value.tasks.count.toLocaleString('en-US'),
|
||||
total: stats.value.tasks.total.toLocaleString('en-US'),
|
||||
ratio: getPercentLabel(stats.value.tasks.ratio)
|
||||
count: stats.value.tasks.count.toLocaleString("en-US"),
|
||||
total: stats.value.tasks.total.toLocaleString("en-US"),
|
||||
ratio: getPercentLabel(stats.value.tasks.ratio),
|
||||
},
|
||||
started_at: getMoment(stats.value.started_at).format('YYYY-MM-DD HH:mm'),
|
||||
completed_at: getMoment(stats.value.completed_at).format('YYYY-MM-DD HH:mm'),
|
||||
started_at: getMoment(stats.value.started_at).format("YYYY-MM-DD HH:mm"),
|
||||
completed_at: getMoment(stats.value.completed_at).format(
|
||||
"YYYY-MM-DD HH:mm",
|
||||
),
|
||||
case_duration: {
|
||||
min: getTimeLabel(stats.value.case_duration.min),
|
||||
max: getTimeLabel(stats.value.case_duration.max),
|
||||
average: getTimeLabel(stats.value.case_duration.average),
|
||||
median: getTimeLabel(stats.value.case_duration.median),
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
const params = route.params;
|
||||
const file = route.meta.file;
|
||||
const isCheckPage = route.name.includes('Check');
|
||||
const isCheckPage = route.name.includes("Check");
|
||||
|
||||
switch (params.type) {
|
||||
case 'log':
|
||||
case "log":
|
||||
logId.value = isCheckPage ? file.parent.id : params.fileId;
|
||||
break;
|
||||
case 'filter':
|
||||
case "filter":
|
||||
createFilterId.value = isCheckPage ? file.parent.id : params.fileId;
|
||||
break;
|
||||
}
|
||||
@@ -169,6 +239,6 @@ onMounted(async () => {
|
||||
<style scoped>
|
||||
@reference "../../assets/tailwind.css";
|
||||
:deep(.p-progressbar .p-progressbar-value) {
|
||||
@apply bg-neutral-900
|
||||
@apply bg-neutral-900;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user