forked from verdnatura/salix-front
Create route summary
This commit is contained in:
parent
8f47a0bc2b
commit
661e9d4368
|
@ -809,7 +809,8 @@ export default {
|
||||||
cmrsList: 'External CMRs list',
|
cmrsList: 'External CMRs list',
|
||||||
RouteList: 'List',
|
RouteList: 'List',
|
||||||
create: 'Create',
|
create: 'Create',
|
||||||
basicData: 'Basic Data'
|
basicData: 'Basic Data',
|
||||||
|
summary: 'Summary'
|
||||||
},
|
},
|
||||||
cmr: {
|
cmr: {
|
||||||
list: {
|
list: {
|
||||||
|
|
|
@ -809,6 +809,7 @@ export default {
|
||||||
RouteList: 'Listado',
|
RouteList: 'Listado',
|
||||||
create: 'Crear',
|
create: 'Crear',
|
||||||
basicData: 'Datos básicos',
|
basicData: 'Datos básicos',
|
||||||
|
summary: 'Summary',
|
||||||
},
|
},
|
||||||
cmr: {
|
cmr: {
|
||||||
list: {
|
list: {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useStateStore } from 'stores/useStateStore';
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import LeftMenu from 'components/LeftMenu.vue';
|
import LeftMenu from 'components/LeftMenu.vue';
|
||||||
|
import RouteDescriptor from 'pages/Route/Card/RouteDescriptor.vue';
|
||||||
// import ShelvingDescriptor from 'pages/Shelving/Card/ShelvingDescriptor.vue';
|
// import ShelvingDescriptor from 'pages/Shelving/Card/ShelvingDescriptor.vue';
|
||||||
|
|
||||||
const stateStore = useStateStore();
|
const stateStore = useStateStore();
|
||||||
|
@ -8,7 +9,7 @@ const stateStore = useStateStore();
|
||||||
<template>
|
<template>
|
||||||
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||||
<QScrollArea class="fit">
|
<QScrollArea class="fit">
|
||||||
<!-- <ShelvingDescriptor />-->
|
<RouteDescriptor />
|
||||||
<QSeparator />
|
<QSeparator />
|
||||||
<LeftMenu source="card" />
|
<LeftMenu source="card" />
|
||||||
</QScrollArea>
|
</QScrollArea>
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import CardDescriptor from 'components/ui/CardDescriptor.vue';
|
||||||
|
import VnLv from 'components/ui/VnLv.vue';
|
||||||
|
import useCardDescription from 'composables/useCardDescription';
|
||||||
|
import { dashIfEmpty, toDate } from 'src/filters';
|
||||||
|
import RouteDescriptorMenu from 'pages/Route/Card/RouteDescriptorMenu.vue';
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const entityId = computed(() => {
|
||||||
|
return $props.id || route.params.id;
|
||||||
|
});
|
||||||
|
|
||||||
|
const filter = {
|
||||||
|
fields: [
|
||||||
|
'id',
|
||||||
|
'workerFk',
|
||||||
|
'agencyModeFk',
|
||||||
|
'created',
|
||||||
|
'm3',
|
||||||
|
'warehouseFk',
|
||||||
|
'description',
|
||||||
|
'vehicleFk',
|
||||||
|
'kmStart',
|
||||||
|
'kmEnd',
|
||||||
|
'started',
|
||||||
|
'finished',
|
||||||
|
'cost',
|
||||||
|
'zoneFk',
|
||||||
|
'isOk',
|
||||||
|
],
|
||||||
|
include: [
|
||||||
|
{ relation: 'agencyMode', scope: { fields: ['id', 'name'] } },
|
||||||
|
{
|
||||||
|
relation: 'vehicle',
|
||||||
|
scope: { fields: ['id', 'm3'] },
|
||||||
|
},
|
||||||
|
{ relation: 'zone', scope: { fields: ['id', 'name'] } },
|
||||||
|
{
|
||||||
|
relation: 'worker',
|
||||||
|
scope: {
|
||||||
|
fields: ['id'],
|
||||||
|
include: {
|
||||||
|
relation: 'user',
|
||||||
|
scope: {
|
||||||
|
fields: ['id'],
|
||||||
|
include: { relation: 'emailUser', scope: { fields: ['email'] } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const data = ref(useCardDescription());
|
||||||
|
const setData = (entity) => (data.value = useCardDescription(entity.code, entity.id));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CardDescriptor
|
||||||
|
module="Route"
|
||||||
|
:url="`Routes/${entityId}`"
|
||||||
|
:filter="filter"
|
||||||
|
:title="data.title"
|
||||||
|
:subtitle="data.subtitle"
|
||||||
|
data-key="Routes"
|
||||||
|
@on-fetch="setData"
|
||||||
|
>
|
||||||
|
<template #body="{ entity }">
|
||||||
|
<VnLv :label="t('Date')" :value="toDate(entity?.created)" />
|
||||||
|
<VnLv :label="t('Agency')" :value="entity?.agencyMode?.name" />
|
||||||
|
<VnLv :label="t('Zone')" :value="entity?.zone?.name" />
|
||||||
|
<VnLv
|
||||||
|
:label="t('Volume')"
|
||||||
|
:value="`${dashIfEmpty(entity?.m3)} / ${dashIfEmpty(
|
||||||
|
entity?.vehicle?.m3
|
||||||
|
)} m³`"
|
||||||
|
/>
|
||||||
|
<VnLv :label="t('Description')" :value="entity?.description" />
|
||||||
|
</template>
|
||||||
|
<template #menu="{ entity }">
|
||||||
|
<RouteDescriptorMenu :route="entity" />
|
||||||
|
</template>
|
||||||
|
</CardDescriptor>
|
||||||
|
</template>
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Date: Fecha
|
||||||
|
Agency: Agencia
|
||||||
|
Zone: Zona
|
||||||
|
Volume: Volumen
|
||||||
|
Description: Descripción
|
||||||
|
</i18n>
|
|
@ -0,0 +1,63 @@
|
||||||
|
<script setup>
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
route: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const quasar = useQuasar();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
function confirmRemove() {
|
||||||
|
quasar
|
||||||
|
.dialog({
|
||||||
|
component: VnConfirm,
|
||||||
|
componentProps: {
|
||||||
|
title: t('confirmDeletion'),
|
||||||
|
message: t('confirmDeletionMessage'),
|
||||||
|
promise: remove,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.onOk(async () => await router.push({ name: 'RouteList' }));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function remove() {
|
||||||
|
if (!props.route.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await axios.delete(`Routes/${props.route.id}`);
|
||||||
|
quasar.notify({
|
||||||
|
message: t('globals.dataDeleted'),
|
||||||
|
type: 'positive',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add reports
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<QItem @click="confirmRemove" v-ripple clickable>
|
||||||
|
<QItemSection avatar>
|
||||||
|
<QIcon name="delete" />
|
||||||
|
</QItemSection>
|
||||||
|
<QItemSection>{{ t('deleteRoute') }}</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
en:
|
||||||
|
confirmDeletion: Confirm deletion
|
||||||
|
confirmDeletionMessage: Are you sure you want to delete this route?
|
||||||
|
deleteRoute: Delete route
|
||||||
|
es:
|
||||||
|
confirmDeletion: Confirmar eliminación,
|
||||||
|
confirmDeletionMessage: Seguro que quieres eliminar esta ruta?
|
||||||
|
deleteRoute: Eliminar ruta
|
||||||
|
</i18n>
|
|
@ -0,0 +1,15 @@
|
||||||
|
<script setup>
|
||||||
|
import RouteDescriptor from 'pages/Route/Card/RouteDescriptor.vue';
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<QPopupProxy>
|
||||||
|
<RouteDescriptor v-if="$props.id" :id="$props.id" />
|
||||||
|
</QPopupProxy>
|
||||||
|
</template>
|
|
@ -72,8 +72,7 @@ const routeFilter = {
|
||||||
const onSave = (data, response) => {
|
const onSave = (data, response) => {
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
axios.post(`Routes/${response.data?.id}/updateWorkCenter`);
|
axios.post(`Routes/${response.data?.id}/updateWorkCenter`);
|
||||||
// TODO: Add summary
|
router.push({ name: 'RouteSummary', params: { id: response.data?.id } });
|
||||||
// router.push({ name: 'RouteSummary', params: { id: response.data?.id } });
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -0,0 +1,314 @@
|
||||||
|
<script setup>
|
||||||
|
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
import CardSummary from 'components/ui/CardSummary.vue';
|
||||||
|
import VnLv from 'components/ui/VnLv.vue';
|
||||||
|
import { QIcon } from 'quasar';
|
||||||
|
import { dashIfEmpty, toCurrency, toDate, toHour } from 'src/filters';
|
||||||
|
import WorkerDescriptorProxy from 'pages/Worker/Card/WorkerDescriptorProxy.vue';
|
||||||
|
import axios from 'axios';
|
||||||
|
import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
|
||||||
|
import TicketDescriptorProxy from 'pages/Ticket/Card/TicketDescriptorProxy.vue';
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const route = useRoute();
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const entityId = computed(() => $props.id || route.params.id);
|
||||||
|
const isDialog = Boolean($props.id);
|
||||||
|
const hideRightDrawer = () => {
|
||||||
|
if (!isDialog) {
|
||||||
|
stateStore.rightDrawer = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
onMounted(hideRightDrawer);
|
||||||
|
onUnmounted(hideRightDrawer);
|
||||||
|
const getTotalPackages = (tickets) => {
|
||||||
|
return (tickets || []).reduce((sum, ticket) => sum + ticket.packages, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ticketColumns = ref([
|
||||||
|
{
|
||||||
|
name: 'order',
|
||||||
|
label: t('route.summary.order'),
|
||||||
|
field: (row) => dashIfEmpty(row?.priority),
|
||||||
|
sortable: false,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'street',
|
||||||
|
label: t('route.summary.street'),
|
||||||
|
field: (row) => row?.street,
|
||||||
|
sortable: false,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'city',
|
||||||
|
label: t('route.summary.city'),
|
||||||
|
field: (row) => row?.city,
|
||||||
|
sortable: false,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'pc',
|
||||||
|
label: t('route.summary.pc'),
|
||||||
|
field: (row) => row?.postalCode,
|
||||||
|
sortable: false,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'client',
|
||||||
|
label: t('route.summary.client'),
|
||||||
|
field: (row) => row?.nickname,
|
||||||
|
sortable: false,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'warehouse',
|
||||||
|
label: t('route.summary.warehouse'),
|
||||||
|
field: (row) => row?.warehouseName,
|
||||||
|
sortable: false,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'packages',
|
||||||
|
label: t('route.summary.packages'),
|
||||||
|
field: (row) => row?.packages,
|
||||||
|
sortable: false,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'volume',
|
||||||
|
label: t('route.summary.m3'),
|
||||||
|
field: (row) => row?.volume,
|
||||||
|
sortable: false,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'packaging',
|
||||||
|
label: t('route.summary.packaging'),
|
||||||
|
field: (row) => row?.ipt,
|
||||||
|
sortable: false,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ticket',
|
||||||
|
label: t('route.summary.ticket'),
|
||||||
|
field: (row) => row?.id,
|
||||||
|
sortable: false,
|
||||||
|
align: 'right',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'observations',
|
||||||
|
label: '',
|
||||||
|
field: (row) => row?.ticketObservation,
|
||||||
|
sortable: false,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const openBuscaman = async (route, ticket) => {
|
||||||
|
if (!route.vehicleFk) throw new Error(`The route doesn't have a vehicle`);
|
||||||
|
const response = await axios.get(`Routes/${route.vehicleFk}/getDeliveryPoint`);
|
||||||
|
if (!response.data)
|
||||||
|
throw new Error(`The route's vehicle doesn't have a delivery point`);
|
||||||
|
|
||||||
|
const address = `${response.data}+to:${ticket.postalCode} ${ticket.city} ${ticket.street}`;
|
||||||
|
window.open(
|
||||||
|
'https://gps.buscalia.com/usuario/localizar.aspx?bmi=true&addr=' +
|
||||||
|
encodeURI(address),
|
||||||
|
'_blank'
|
||||||
|
);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="q-pa-md">
|
||||||
|
<CardSummary ref="summary" :url="`Routes/${entityId}/summary`">
|
||||||
|
<template #header-left>
|
||||||
|
<RouterLink :to="{ name: `RouteSummary`, params: { id: entityId } }">
|
||||||
|
<QIcon name="open_in_new" color="white" size="sm" />
|
||||||
|
</RouterLink>
|
||||||
|
</template>
|
||||||
|
<template #header="{ entity }">
|
||||||
|
<span>{{ `${entity?.route.id} - ${entity?.route?.description}` }}</span>
|
||||||
|
</template>
|
||||||
|
<template #body="{ entity }">
|
||||||
|
<QCard class="vn-one">
|
||||||
|
<VnLv :label="t('ID')" :value="entity?.route.id" />
|
||||||
|
<VnLv
|
||||||
|
:label="t('route.summary.date')"
|
||||||
|
:value="toDate(entity?.route.created)"
|
||||||
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('route.summary.agency')"
|
||||||
|
:value="entity?.route?.agencyMode?.name"
|
||||||
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('route.summary.vehicle')"
|
||||||
|
:value="entity.route?.vehicle?.numberPlate"
|
||||||
|
/>
|
||||||
|
<VnLv :label="t('route.summary.driver')">
|
||||||
|
<template #value>
|
||||||
|
<span class="link">
|
||||||
|
{{ dashIfEmpty(entity?.route?.worker?.user?.name) }}
|
||||||
|
<WorkerDescriptorProxy :id="entity.route?.workerFk" />
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</VnLv>
|
||||||
|
<VnLv
|
||||||
|
:label="t('route.summary.cost')"
|
||||||
|
:value="toCurrency(entity.route?.cost)"
|
||||||
|
/>
|
||||||
|
</QCard>
|
||||||
|
<QCard class="vn-one">
|
||||||
|
<VnLv
|
||||||
|
:label="t('route.summary.started')"
|
||||||
|
:value="toHour(entity?.route.started)"
|
||||||
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('route.summary.finished')"
|
||||||
|
:value="toHour(entity?.route.finished)"
|
||||||
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('route.summary.kmStart')"
|
||||||
|
:value="dashIfEmpty(entity?.route?.kmStart)"
|
||||||
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('route.summary.kmEnd')"
|
||||||
|
:value="dashIfEmpty(entity?.route?.kmEnd)"
|
||||||
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('route.summary.volume')"
|
||||||
|
:value="`${dashIfEmpty(entity?.route?.m3)} / ${dashIfEmpty(
|
||||||
|
entity?.route?.vehicle?.m3
|
||||||
|
)} m³`"
|
||||||
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('route.summary.packages')"
|
||||||
|
:value="getTotalPackages(entity.tickets)"
|
||||||
|
/>
|
||||||
|
</QCard>
|
||||||
|
<QCard class="vn-one">
|
||||||
|
<div class="header">
|
||||||
|
{{ t('route.summary.description') }}
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
{{ dashIfEmpty(entity?.route?.description) }}
|
||||||
|
</p>
|
||||||
|
</QCard>
|
||||||
|
|
||||||
|
<QCard class="vn-max">
|
||||||
|
<div class="header">
|
||||||
|
{{ t('route.summary.tickets') }}
|
||||||
|
</div>
|
||||||
|
<QTable
|
||||||
|
:columns="ticketColumns"
|
||||||
|
:rows="entity?.tickets"
|
||||||
|
:rows-per-page-options="[0]"
|
||||||
|
row-key="id"
|
||||||
|
flat
|
||||||
|
hide-pagination
|
||||||
|
>
|
||||||
|
<template #body-cell-city="{ value, row }">
|
||||||
|
<QTd auto-width>
|
||||||
|
<span
|
||||||
|
class="text-primary cursor-pointer"
|
||||||
|
@click="openBuscaman(entity?.route, row)"
|
||||||
|
>
|
||||||
|
{{ value }}
|
||||||
|
</span>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-client="{ value, row }">
|
||||||
|
<QTd auto-width>
|
||||||
|
<span class="text-primary cursor-pointer">
|
||||||
|
{{ value }}
|
||||||
|
<CustomerDescriptorProxy :id="row?.clientFk" />
|
||||||
|
</span>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-ticket="{ value, row }">
|
||||||
|
<QTd auto-width>
|
||||||
|
<span class="text-primary cursor-pointer">
|
||||||
|
{{ value }}
|
||||||
|
<TicketDescriptorProxy :id="row?.id" />
|
||||||
|
</span>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-observations="{ value }">
|
||||||
|
<QTd auto-width>
|
||||||
|
<QIcon
|
||||||
|
v-if="value"
|
||||||
|
name="vn:notes"
|
||||||
|
color="primary"
|
||||||
|
class="cursor-pointer"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ value }}</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
</QTable>
|
||||||
|
</QCard>
|
||||||
|
</template>
|
||||||
|
</CardSummary>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<i18n>
|
||||||
|
en:
|
||||||
|
route:
|
||||||
|
summary:
|
||||||
|
date: Date
|
||||||
|
agency: Agency
|
||||||
|
vehicle: Vehicle
|
||||||
|
driver: Driver
|
||||||
|
cost: Cost
|
||||||
|
started: Started time
|
||||||
|
finished: Finished time
|
||||||
|
kmStart: Km start
|
||||||
|
kmEnd: Km end
|
||||||
|
volume: Volume
|
||||||
|
packages: Packages
|
||||||
|
description: Description
|
||||||
|
tickets: Tickets
|
||||||
|
order: Order
|
||||||
|
street: Street
|
||||||
|
city: City
|
||||||
|
pc: PC
|
||||||
|
client: Client
|
||||||
|
warehouse: Warehouse
|
||||||
|
m3: m³
|
||||||
|
packaging: Packaging
|
||||||
|
ticket: Ticket
|
||||||
|
es:
|
||||||
|
route:
|
||||||
|
summary:
|
||||||
|
date: Fecha
|
||||||
|
agency: Agencia
|
||||||
|
vehicle: Vehículo
|
||||||
|
driver: Conductor
|
||||||
|
cost: Costo
|
||||||
|
started: Hora inicio
|
||||||
|
finished: Hora fin
|
||||||
|
kmStart: Km inicio
|
||||||
|
kmEnd: Km fin
|
||||||
|
volume: Volumen
|
||||||
|
packages: Bultos
|
||||||
|
description: Descripción
|
||||||
|
tickets: Tickets
|
||||||
|
order: Orden
|
||||||
|
street: Dirección fiscal
|
||||||
|
city: Población
|
||||||
|
pc: CP
|
||||||
|
client: Cliente
|
||||||
|
warehouse: Almacén
|
||||||
|
packaging: Encajado
|
||||||
|
</i18n>
|
|
@ -0,0 +1,29 @@
|
||||||
|
<script setup>
|
||||||
|
import { useDialogPluginComponent } from 'quasar';
|
||||||
|
import RouteSummary from 'pages/Route/Card/RouteSummary.vue';
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
defineEmits([...useDialogPluginComponent.emits]);
|
||||||
|
|
||||||
|
const { dialogRef, onDialogHide } = useDialogPluginComponent();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<QDialog ref="dialogRef" @hide="onDialogHide">
|
||||||
|
<RouteSummary v-if="$props.id" :id="$props.id" />
|
||||||
|
</QDialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.q-dialog .route .header {
|
||||||
|
position: sticky;
|
||||||
|
z-index: $z-max;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -13,12 +13,13 @@ import VnInputTime from 'components/common/VnInputTime.vue';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import RouteSearchbar from 'pages/Route/Card/RouteSearchbar.vue';
|
import RouteSearchbar from 'pages/Route/Card/RouteSearchbar.vue';
|
||||||
import RouteFilter from 'pages/Route/Card/RouteFilter.vue';
|
import RouteFilter from 'pages/Route/Card/RouteFilter.vue';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
import RouteSummaryDialog from 'pages/Route/Card/RouteSummaryDialog.vue';
|
||||||
|
|
||||||
const stateStore = useStateStore();
|
const stateStore = useStateStore();
|
||||||
// const router = useRouter();
|
|
||||||
// const quasar = useQuasar();
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { validate } = useValidator();
|
const { validate } = useValidator();
|
||||||
|
const quasar = useQuasar();
|
||||||
|
|
||||||
const to = Date.vnNew();
|
const to = Date.vnNew();
|
||||||
to.setDate(to.getDate() + 1);
|
to.setDate(to.getDate() + 1);
|
||||||
|
@ -98,6 +99,12 @@ const columns = computed(() => [
|
||||||
sortable: true,
|
sortable: true,
|
||||||
align: 'left',
|
align: 'left',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'actions',
|
||||||
|
label: '',
|
||||||
|
sortable: false,
|
||||||
|
align: 'right',
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const refreshKey = ref(0);
|
const refreshKey = ref(0);
|
||||||
|
@ -151,6 +158,18 @@ const markAsServed = () => {
|
||||||
refreshKey.value++;
|
refreshKey.value++;
|
||||||
startingDate.value = null;
|
startingDate.value = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function previewRoute(id) {
|
||||||
|
if (!id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
quasar.dialog({
|
||||||
|
component: RouteSummaryDialog,
|
||||||
|
componentProps: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -187,7 +206,7 @@ const markAsServed = () => {
|
||||||
autofocus
|
autofocus
|
||||||
/>
|
/>
|
||||||
</QCardSection>
|
</QCardSection>
|
||||||
|
<!-- TODO: Add report -->
|
||||||
<QCardActions align="right">
|
<QCardActions align="right">
|
||||||
<QBtn flat :label="t('Cancel')" v-close-popup class="text-primary" />
|
<QBtn flat :label="t('Cancel')" v-close-popup class="text-primary" />
|
||||||
<QBtn color="primary" v-close-popup @click="cloneRoutes">
|
<QBtn color="primary" v-close-popup @click="cloneRoutes">
|
||||||
|
@ -232,213 +251,237 @@ const markAsServed = () => {
|
||||||
</QBtn>
|
</QBtn>
|
||||||
</div>
|
</div>
|
||||||
</QToolbar>
|
</QToolbar>
|
||||||
<VnPaginate
|
<div class="route-list">
|
||||||
:key="refreshKey"
|
<VnPaginate
|
||||||
data-key="RouteList"
|
:key="refreshKey"
|
||||||
url="Routes/filter"
|
data-key="RouteList"
|
||||||
:order="['created DESC', 'id DESC']"
|
url="Routes/filter"
|
||||||
:limit="20"
|
:order="['created DESC', 'id DESC']"
|
||||||
auto-load
|
:limit="20"
|
||||||
>
|
auto-load
|
||||||
<template #body="{ rows }">
|
>
|
||||||
<div class="q-pa-md">
|
<template #body="{ rows }">
|
||||||
<QTable
|
<div class="q-pa-md">
|
||||||
v-model:selected="selectedRows"
|
<QTable
|
||||||
:columns="columns"
|
v-model:selected="selectedRows"
|
||||||
:rows="rows"
|
:columns="columns"
|
||||||
flat
|
:rows="rows"
|
||||||
row-key="id"
|
flat
|
||||||
selection="multiple"
|
row-key="id"
|
||||||
:rows-per-page-options="[0]"
|
selection="multiple"
|
||||||
>
|
:rows-per-page-options="[0]"
|
||||||
<template #body-cell-worker="props">
|
hide-pagination
|
||||||
<QTd :props="props">
|
>
|
||||||
{{ props.row?.workerUserName }}
|
<template #body-cell-worker="props">
|
||||||
<QPopupEdit
|
<QTd :props="props">
|
||||||
:model-value="props.row.workerFk"
|
{{ props.row?.workerUserName }}
|
||||||
v-slot="scope"
|
<QPopupEdit
|
||||||
buttons
|
:model-value="props.row.workerFk"
|
||||||
@update:model-value="
|
v-slot="scope"
|
||||||
(worker) => updateWorker(props.row, worker)
|
buttons
|
||||||
"
|
@update:model-value="
|
||||||
>
|
(worker) => updateWorker(props.row, worker)
|
||||||
<VnSelectFilter
|
"
|
||||||
:label="t('Worker')"
|
|
||||||
v-model="scope.value"
|
|
||||||
:options="workers"
|
|
||||||
option-value="id"
|
|
||||||
option-label="name"
|
|
||||||
hide-selected
|
|
||||||
autofocus
|
|
||||||
:emit-value="false"
|
|
||||||
:rules="validate('Route.workerFk')"
|
|
||||||
:is-clearable="false"
|
|
||||||
@keyup.enter="scope.set"
|
|
||||||
@focus="($event) => $event.target.select()"
|
|
||||||
>
|
>
|
||||||
<template #option="{ opt, itemProps }">
|
<VnSelectFilter
|
||||||
<QItem
|
:label="t('Worker')"
|
||||||
v-bind="itemProps"
|
v-model="scope.value"
|
||||||
class="q-pa-xs row items-center"
|
:options="workers"
|
||||||
>
|
option-value="id"
|
||||||
<QItemSection
|
option-label="name"
|
||||||
class="col-9 justify-center"
|
hide-selected
|
||||||
|
autofocus
|
||||||
|
:emit-value="false"
|
||||||
|
:rules="validate('Route.workerFk')"
|
||||||
|
:is-clearable="false"
|
||||||
|
@keyup.enter="scope.set"
|
||||||
|
@focus="($event) => $event.target.select()"
|
||||||
|
>
|
||||||
|
<template #option="{ opt, itemProps }">
|
||||||
|
<QItem
|
||||||
|
v-bind="itemProps"
|
||||||
|
class="q-pa-xs row items-center"
|
||||||
>
|
>
|
||||||
<span>{{ opt.name }}</span>
|
<QItemSection
|
||||||
<span class="text-grey">{{
|
class="col-9 justify-center"
|
||||||
opt.nickname
|
>
|
||||||
}}</span>
|
<span>{{ opt.name }}</span>
|
||||||
</QItemSection>
|
<span class="text-grey">{{
|
||||||
</QItem>
|
opt.nickname
|
||||||
</template>
|
}}</span>
|
||||||
</VnSelectFilter>
|
</QItemSection>
|
||||||
</QPopupEdit>
|
</QItem>
|
||||||
</QTd>
|
</template>
|
||||||
</template>
|
</VnSelectFilter>
|
||||||
<template #body-cell-agency="props">
|
</QPopupEdit>
|
||||||
<QTd :props="props">
|
</QTd>
|
||||||
{{ props.row?.agencyName }}
|
</template>
|
||||||
<QPopupEdit
|
<template #body-cell-agency="props">
|
||||||
:model-value="props.row.agencyModeFk"
|
<QTd :props="props">
|
||||||
v-slot="scope"
|
{{ props.row?.agencyName }}
|
||||||
buttons
|
<QPopupEdit
|
||||||
@update:model-value="
|
:model-value="props.row.agencyModeFk"
|
||||||
(agency) => updateAgency(props.row, agency)
|
v-slot="scope"
|
||||||
"
|
buttons
|
||||||
>
|
@update:model-value="
|
||||||
<VnSelectFilter
|
(agency) => updateAgency(props.row, agency)
|
||||||
:label="t('Agency')"
|
"
|
||||||
v-model="scope.value"
|
>
|
||||||
:options="agencyList"
|
<VnSelectFilter
|
||||||
option-value="id"
|
:label="t('Agency')"
|
||||||
option-label="name"
|
v-model="scope.value"
|
||||||
hide-selected
|
:options="agencyList"
|
||||||
autofocus
|
option-value="id"
|
||||||
:emit-value="false"
|
option-label="name"
|
||||||
:rules="validate('route.agencyFk')"
|
hide-selected
|
||||||
:is-clearable="false"
|
autofocus
|
||||||
@keyup.enter="scope.set"
|
:emit-value="false"
|
||||||
@focus="($event) => $event.target.select()"
|
:rules="validate('route.agencyFk')"
|
||||||
/>
|
:is-clearable="false"
|
||||||
</QPopupEdit>
|
@keyup.enter="scope.set"
|
||||||
</QTd>
|
@focus="($event) => $event.target.select()"
|
||||||
</template>
|
/>
|
||||||
<template #body-cell-vehicle="props">
|
</QPopupEdit>
|
||||||
<QTd :props="props">
|
</QTd>
|
||||||
{{ props.row?.vehiclePlateNumber }}
|
</template>
|
||||||
<QPopupEdit
|
<template #body-cell-vehicle="props">
|
||||||
:model-value="props.row.vehicleFk"
|
<QTd :props="props">
|
||||||
v-slot="scope"
|
{{ props.row?.vehiclePlateNumber }}
|
||||||
buttons
|
<QPopupEdit
|
||||||
@update:model-value="
|
:model-value="props.row.vehicleFk"
|
||||||
(vehicle) => updateVehicle(props.row, vehicle)
|
v-slot="scope"
|
||||||
"
|
buttons
|
||||||
>
|
@update:model-value="
|
||||||
<VnSelectFilter
|
(vehicle) => updateVehicle(props.row, vehicle)
|
||||||
:label="t('Vehicle')"
|
"
|
||||||
v-model="scope.value"
|
>
|
||||||
:options="vehicleList"
|
<VnSelectFilter
|
||||||
option-value="id"
|
:label="t('Vehicle')"
|
||||||
option-label="numberPlate"
|
v-model="scope.value"
|
||||||
hide-selected
|
:options="vehicleList"
|
||||||
autofocus
|
option-value="id"
|
||||||
:emit-value="false"
|
option-label="numberPlate"
|
||||||
:rules="validate('route.vehicleFk')"
|
hide-selected
|
||||||
:is-clearable="false"
|
autofocus
|
||||||
@keyup.enter="scope.set"
|
:emit-value="false"
|
||||||
@focus="($event) => $event.target.select()"
|
:rules="validate('route.vehicleFk')"
|
||||||
/>
|
:is-clearable="false"
|
||||||
</QPopupEdit>
|
@keyup.enter="scope.set"
|
||||||
</QTd>
|
@focus="($event) => $event.target.select()"
|
||||||
</template>
|
/>
|
||||||
<template #body-cell-date="props">
|
</QPopupEdit>
|
||||||
<QTd :props="props">
|
</QTd>
|
||||||
{{ toDate(props.row?.created) }}
|
</template>
|
||||||
<QPopupEdit
|
<template #body-cell-date="props">
|
||||||
v-model="props.row.created"
|
<QTd :props="props">
|
||||||
v-slot="scope"
|
{{ toDate(props.row?.created) }}
|
||||||
@update:model-value="updateRoute(props.row)"
|
<QPopupEdit
|
||||||
buttons
|
v-model="props.row.created"
|
||||||
>
|
v-slot="scope"
|
||||||
<VnInputDate
|
@update:model-value="updateRoute(props.row)"
|
||||||
v-model="scope.value"
|
buttons
|
||||||
autofocus
|
>
|
||||||
:label="t('Date')"
|
<VnInputDate
|
||||||
:rules="validate('route.created')"
|
v-model="scope.value"
|
||||||
:is-clearable="false"
|
autofocus
|
||||||
@keyup.enter="scope.set"
|
:label="t('Date')"
|
||||||
@focus="($event) => $event.target.select()"
|
:rules="validate('route.created')"
|
||||||
/>
|
:is-clearable="false"
|
||||||
</QPopupEdit>
|
@keyup.enter="scope.set"
|
||||||
</QTd>
|
@focus="($event) => $event.target.select()"
|
||||||
</template>
|
/>
|
||||||
<template #body-cell-description="props">
|
</QPopupEdit>
|
||||||
<QTd :props="props">
|
</QTd>
|
||||||
{{ props.row?.description }}
|
</template>
|
||||||
<QPopupEdit
|
<template #body-cell-description="props">
|
||||||
v-model="props.row.description"
|
<QTd :props="props">
|
||||||
v-slot="scope"
|
{{ props.row?.description }}
|
||||||
@update:model-value="updateRoute(props.row)"
|
<QPopupEdit
|
||||||
buttons
|
v-model="props.row.description"
|
||||||
>
|
v-slot="scope"
|
||||||
<VnInput
|
@update:model-value="updateRoute(props.row)"
|
||||||
v-model="scope.value"
|
buttons
|
||||||
autofocus
|
>
|
||||||
:label="t('Description')"
|
<VnInput
|
||||||
:rules="validate('route.description')"
|
v-model="scope.value"
|
||||||
:is-clearable="false"
|
autofocus
|
||||||
@keyup.enter="scope.set"
|
:label="t('Description')"
|
||||||
@focus="($event) => $event.target.select()"
|
:rules="validate('route.description')"
|
||||||
/>
|
:is-clearable="false"
|
||||||
</QPopupEdit>
|
@keyup.enter="scope.set"
|
||||||
</QTd>
|
@focus="($event) => $event.target.select()"
|
||||||
</template>
|
/>
|
||||||
<template #body-cell-started="props">
|
</QPopupEdit>
|
||||||
<QTd :props="props">
|
</QTd>
|
||||||
{{ toHour(props.row.started) }}
|
</template>
|
||||||
<QPopupEdit
|
<template #body-cell-started="props">
|
||||||
v-model="props.row.started"
|
<QTd :props="props">
|
||||||
v-slot="scope"
|
{{ toHour(props.row.started) }}
|
||||||
buttons
|
<QPopupEdit
|
||||||
@update:model-value="updateRoute(props.row)"
|
v-model="props.row.started"
|
||||||
>
|
v-slot="scope"
|
||||||
<VnInputTime
|
buttons
|
||||||
v-model="scope.value"
|
@update:model-value="updateRoute(props.row)"
|
||||||
autofocus
|
>
|
||||||
:label="t('Hour started')"
|
<VnInputTime
|
||||||
:rules="validate('route.started')"
|
v-model="scope.value"
|
||||||
:is-clearable="false"
|
autofocus
|
||||||
@keyup.enter="scope.set"
|
:label="t('Hour started')"
|
||||||
@focus="($event) => $event.target.select()"
|
:rules="validate('route.started')"
|
||||||
/>
|
:is-clearable="false"
|
||||||
</QPopupEdit>
|
@keyup.enter="scope.set"
|
||||||
</QTd>
|
@focus="($event) => $event.target.select()"
|
||||||
</template>
|
/>
|
||||||
<template #body-cell-finished="props">
|
</QPopupEdit>
|
||||||
<QTd :props="props">
|
</QTd>
|
||||||
{{ toHour(props.row.finished) }}
|
</template>
|
||||||
<QPopupEdit
|
<template #body-cell-finished="props">
|
||||||
v-model="props.row.finished"
|
<QTd :props="props">
|
||||||
v-slot="scope"
|
{{ toHour(props.row.finished) }}
|
||||||
buttons
|
<QPopupEdit
|
||||||
@update:model-value="updateRoute(props.row)"
|
v-model="props.row.finished"
|
||||||
>
|
v-slot="scope"
|
||||||
<VnInputTime
|
buttons
|
||||||
v-model="scope.value"
|
@update:model-value="updateRoute(props.row)"
|
||||||
autofocus
|
>
|
||||||
:label="t('Hour finished')"
|
<VnInputTime
|
||||||
:rules="validate('route.finished')"
|
v-model="scope.value"
|
||||||
:is-clearable="false"
|
autofocus
|
||||||
@keyup.enter="scope.set"
|
:label="t('Hour finished')"
|
||||||
@focus="($event) => $event.target.select()"
|
:rules="validate('route.finished')"
|
||||||
/>
|
:is-clearable="false"
|
||||||
</QPopupEdit>
|
@keyup.enter="scope.set"
|
||||||
</QTd>
|
@focus="($event) => $event.target.select()"
|
||||||
</template>
|
/>
|
||||||
</QTable>
|
</QPopupEdit>
|
||||||
</div>
|
</QTd>
|
||||||
</template>
|
</template>
|
||||||
</VnPaginate>
|
<template #body-cell-actions="props">
|
||||||
|
<QTd :props="props">
|
||||||
|
<div class="table-actions">
|
||||||
|
<QIcon
|
||||||
|
name="vn:ticketAdd"
|
||||||
|
size="xs"
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('Add ticket') }}</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
<QIcon
|
||||||
|
name="preview"
|
||||||
|
size="xs"
|
||||||
|
color="primary"
|
||||||
|
@click="previewRoute(props?.row?.id)"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('Preview') }}</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
</div>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
</QTable>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</VnPaginate>
|
||||||
|
</div>
|
||||||
<QPageSticky :offset="[20, 20]">
|
<QPageSticky :offset="[20, 20]">
|
||||||
<RouterLink :to="{ name: 'RouteCreate' }">
|
<RouterLink :to="{ name: 'RouteCreate' }">
|
||||||
<QBtn fab icon="add" color="primary" />
|
<QBtn fab icon="add" color="primary" />
|
||||||
|
@ -451,9 +494,18 @@ const markAsServed = () => {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.card-list {
|
.route-list {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 60em;
|
}
|
||||||
|
|
||||||
|
.table-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
i {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<i18n>
|
<i18n>
|
||||||
|
|
|
@ -11,14 +11,14 @@ export default {
|
||||||
redirect: { name: 'RouteMain' },
|
redirect: { name: 'RouteMain' },
|
||||||
menus: {
|
menus: {
|
||||||
main: ['RouteList', 'CmrList'],
|
main: ['RouteList', 'CmrList'],
|
||||||
card: [],
|
card: ['RouteBasicData'],
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/route',
|
path: '/route',
|
||||||
name: 'RouteMain',
|
name: 'RouteMain',
|
||||||
component: () => import('src/pages/Route/RouteMain.vue'),
|
component: () => import('src/pages/Route/RouteMain.vue'),
|
||||||
redirect: { name: 'CmrList' },
|
redirect: { name: 'RouteList' },
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'cmr',
|
path: 'cmr',
|
||||||
|
@ -34,7 +34,7 @@ export default {
|
||||||
name: 'RouteList',
|
name: 'RouteList',
|
||||||
meta: {
|
meta: {
|
||||||
title: 'RouteList',
|
title: 'RouteList',
|
||||||
icon: 'vn:delivery',
|
icon: 'view_list',
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/Route/RouteList.vue'),
|
component: () => import('src/pages/Route/RouteList.vue'),
|
||||||
},
|
},
|
||||||
|
@ -52,8 +52,7 @@ export default {
|
||||||
name: 'RouteCard',
|
name: 'RouteCard',
|
||||||
path: ':id',
|
path: ':id',
|
||||||
component: () => import('src/pages/Route/Card/RouteCard.vue'),
|
component: () => import('src/pages/Route/Card/RouteCard.vue'),
|
||||||
// TODO: Add summary
|
redirect: { name: 'RouteSummary' },
|
||||||
redirect: { name: 'RouteBasicData' },
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
name: 'RouteBasicData',
|
name: 'RouteBasicData',
|
||||||
|
@ -64,6 +63,15 @@ export default {
|
||||||
},
|
},
|
||||||
component: () => import('pages/Route/Card/RouteForm.vue'),
|
component: () => import('pages/Route/Card/RouteForm.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'RouteSummary',
|
||||||
|
path: 'summary',
|
||||||
|
meta: {
|
||||||
|
title: 'summary',
|
||||||
|
icon: 'open_in_new',
|
||||||
|
},
|
||||||
|
component: () => import('pages/Route/Card/RouteSummary.vue'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
Loading…
Reference in New Issue