208 lines
5.8 KiB
Vue
208 lines
5.8 KiB
Vue
<script setup>
|
|
import { ref, computed, watch, onBeforeMount } from 'vue';
|
|
import { useRoute } from 'vue-router';
|
|
import SkeletonSummary from 'components/ui/SkeletonSummary.vue';
|
|
import VnLv from 'src/components/ui/VnLv.vue';
|
|
import { useArrayData } from 'src/composables/useArrayData';
|
|
import { isDialogOpened } from 'src/filters';
|
|
import VnMoreOptions from './VnMoreOptions.vue';
|
|
|
|
const props = defineProps({
|
|
url: {
|
|
type: String,
|
|
default: '',
|
|
},
|
|
filter: {
|
|
type: Object,
|
|
default: null,
|
|
},
|
|
entityId: {
|
|
type: [Number, String],
|
|
default: null,
|
|
},
|
|
dataKey: {
|
|
type: String,
|
|
default: '',
|
|
},
|
|
moduleName: {
|
|
type: String,
|
|
default: null,
|
|
},
|
|
});
|
|
const emit = defineEmits(['onFetch']);
|
|
const route = useRoute();
|
|
const isSummary = ref();
|
|
const arrayData = useArrayData(props.dataKey, {
|
|
url: props.url,
|
|
filter: props.filter,
|
|
skip: 0,
|
|
});
|
|
const { store } = arrayData;
|
|
const entity = computed(() => (Array.isArray(store.data) ? store.data[0] : store.data));
|
|
const isLoading = ref(false);
|
|
|
|
defineExpose({
|
|
entity,
|
|
fetch,
|
|
});
|
|
|
|
onBeforeMount(async () => {
|
|
isSummary.value = String(route.path).endsWith('/summary');
|
|
await fetch();
|
|
watch(props, async () => await fetch());
|
|
});
|
|
|
|
async function fetch() {
|
|
store.url = props.url;
|
|
store.filter = props.filter ?? {};
|
|
isLoading.value = true;
|
|
const { data } = await arrayData.fetch({ append: false, updateRouter: false });
|
|
emit('onFetch', Array.isArray(data) ? data[0] : data);
|
|
isLoading.value = false;
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="summary container">
|
|
<QCard class="cardSummary">
|
|
<SkeletonSummary v-if="!entity || isLoading" />
|
|
<template v-if="entity && !isLoading">
|
|
<div class="summaryHeader bg-primary q-pa-sm text-weight-bolder">
|
|
<slot name="header-left">
|
|
<router-link
|
|
v-if="isDialogOpened()"
|
|
class="header link"
|
|
:to="{
|
|
name: `${moduleName ?? route.meta.moduleName}Summary`,
|
|
params: { id: entityId || entity.id },
|
|
}"
|
|
>
|
|
<QIcon name="open_in_new" color="white" size="sm" />
|
|
</router-link>
|
|
<span v-else></span>
|
|
</slot>
|
|
<slot name="header" :entity="entity" dense>
|
|
<VnLv :label="`${entity.id} -`" :value="entity.name" />
|
|
</slot>
|
|
<span class="row no-wrap">
|
|
<slot name="header-right" :entity="entity" />
|
|
<VnMoreOptions v-if="$slots.menu && isDialogOpened()">
|
|
<template #menu="{ menuRef }">
|
|
<slot name="menu" :entity="entity" :menu-ref="menuRef" />
|
|
</template>
|
|
</VnMoreOptions>
|
|
</span>
|
|
</div>
|
|
<div class="summaryBody row q-mb-md">
|
|
<slot name="body" :entity="entity" />
|
|
</div>
|
|
</template>
|
|
</QCard>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="scss">
|
|
.summary.container {
|
|
display: flex;
|
|
justify-content: center;
|
|
}
|
|
|
|
.cardSummary {
|
|
width: 100%;
|
|
max-height: 70vh;
|
|
.summaryHeader {
|
|
text-align: center;
|
|
font-size: 20px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
}
|
|
.summaryBody {
|
|
display: flex;
|
|
flex-direction: row;
|
|
flex-wrap: wrap;
|
|
justify-content: space-evenly;
|
|
gap: 10px;
|
|
padding: 10px;
|
|
background-color: var(--vn-section-color);
|
|
|
|
> .q-card.vn-one {
|
|
flex: 1;
|
|
}
|
|
> .q-card.vn-two {
|
|
flex: 40%;
|
|
}
|
|
> .q-card.vn-three {
|
|
flex: 75%;
|
|
}
|
|
> .q-card.vn-max {
|
|
flex: 100%;
|
|
}
|
|
|
|
> .q-card {
|
|
width: 100%;
|
|
background-color: var(--vn-section-color);
|
|
padding: 7px;
|
|
font-size: 16px;
|
|
min-width: 275px;
|
|
box-shadow: none;
|
|
|
|
.vn-label-value {
|
|
&.negative > .value span {
|
|
color: $alert;
|
|
}
|
|
display: flex;
|
|
flex-direction: row;
|
|
margin-top: 2px;
|
|
.label {
|
|
color: var(--vn-label-color);
|
|
width: 9em;
|
|
overflow: hidden;
|
|
white-space: wrap;
|
|
text-overflow: ellipsis;
|
|
margin-right: 10px;
|
|
flex-grow: 0;
|
|
flex-shrink: 0;
|
|
}
|
|
.value {
|
|
color: var(--vn-text-color);
|
|
overflow: hidden;
|
|
}
|
|
}
|
|
.header {
|
|
color: $primary;
|
|
font-weight: bold;
|
|
margin-bottom: 10px;
|
|
font-size: 20px;
|
|
display: inline-block;
|
|
}
|
|
.header.link:hover {
|
|
color: lighten($primary, 20%);
|
|
}
|
|
.q-checkbox {
|
|
& .q-checkbox__label {
|
|
color: var(--vn-text-color);
|
|
}
|
|
& .q-checkbox__inner {
|
|
color: var(--vn-label-color);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@media (max-width: $breakpoint-xs) {
|
|
.summaryBody {
|
|
padding: 0;
|
|
}
|
|
}
|
|
}
|
|
</style>
|
|
<style lang="scss" scoped>
|
|
.summaryHeader .vn-label-value {
|
|
display: flex;
|
|
flex-direction: row;
|
|
}
|
|
.summaryHeader {
|
|
color: $white;
|
|
}
|
|
</style>
|