Merge pull request 'Create Route module' (#73) from feature/routes into dev
Reviewed-on: hyervoni/salix-front-mindshore#73
This commit is contained in:
commit
f66af0e663
|
@ -128,13 +128,14 @@ async function save() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const body = $props.mapper ? $props.mapper(formData.value) : formData.value;
|
const body = $props.mapper ? $props.mapper(formData.value) : formData.value;
|
||||||
|
let response
|
||||||
if ($props.urlCreate) {
|
if ($props.urlCreate) {
|
||||||
await axios.post($props.urlCreate, body);
|
response = await axios.post($props.urlCreate, body);
|
||||||
notify('globals.dataCreated', 'positive');
|
notify('globals.dataCreated', 'positive');
|
||||||
} else {
|
} else {
|
||||||
await axios.patch($props.urlUpdate || $props.url, body);
|
response = await axios.patch($props.urlUpdate || $props.url, body);
|
||||||
}
|
}
|
||||||
emit('onDataSaved', formData.value);
|
emit('onDataSaved', formData.value, response);
|
||||||
originalData.value = JSON.parse(JSON.stringify(formData.value));
|
originalData.value = JSON.parse(JSON.stringify(formData.value));
|
||||||
hasChanges.value = false;
|
hasChanges.value = false;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
<script setup>
|
||||||
|
import {computed, ref} from 'vue';
|
||||||
|
import { toHour} from 'src/filters';
|
||||||
|
import {useI18n} from "vue-i18n";
|
||||||
|
import isValidDate from "filters/isValidDate";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
readonly: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
isOutlined: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { t } = useI18n();
|
||||||
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
const value = computed({
|
||||||
|
get() {
|
||||||
|
return props.modelValue;
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
const [hours, minutes] = value.split(':')
|
||||||
|
const date = new Date()
|
||||||
|
date.setUTCHours(Number.parseInt(hours) || 0, Number.parseInt(minutes) || 0, 0, 0)
|
||||||
|
emit('update:modelValue', value ? date.toISOString() : null);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const onDateUpdate = (date) => {
|
||||||
|
internalValue.value = date;
|
||||||
|
};
|
||||||
|
|
||||||
|
const save = () => {
|
||||||
|
value.value = internalValue.value;
|
||||||
|
};
|
||||||
|
const formatTime = (dateString) => {
|
||||||
|
if (!isValidDate(dateString)){
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
const date = new Date(dateString || '');
|
||||||
|
return `${date.getUTCHours().toString().padStart(2, '0')}:${date.getUTCMinutes().toString().padStart(2, '0')}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const internalValue = ref(formatTime(value))
|
||||||
|
|
||||||
|
const styleAttrs = computed(() => {
|
||||||
|
return props.isOutlined
|
||||||
|
? {
|
||||||
|
dense: true,
|
||||||
|
outlined: true,
|
||||||
|
rounded: true,
|
||||||
|
}
|
||||||
|
: {};
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<QInput
|
||||||
|
class="vn-input-time"
|
||||||
|
rounded
|
||||||
|
readonly
|
||||||
|
:model-value="toHour(value)"
|
||||||
|
v-bind="{ ...$attrs, ...styleAttrs }"
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<QIcon name="event" class="cursor-pointer">
|
||||||
|
<QPopupProxy
|
||||||
|
cover
|
||||||
|
transition-show="scale"
|
||||||
|
transition-hide="scale"
|
||||||
|
:no-parent-event="props.readonly"
|
||||||
|
>
|
||||||
|
<QTime
|
||||||
|
:format24h="false"
|
||||||
|
:model-value="formatTime(value)"
|
||||||
|
@update:model-value="onDateUpdate"
|
||||||
|
>
|
||||||
|
<div class="row items-center justify-end q-gutter-sm">
|
||||||
|
<QBtn :label="t('Cancel')" color="primary" flat v-close-popup />
|
||||||
|
<QBtn label="Ok" color="primary" flat @click="save" v-close-popup />
|
||||||
|
</div>
|
||||||
|
</QTime>
|
||||||
|
</QPopupProxy>
|
||||||
|
</QIcon>
|
||||||
|
</template>
|
||||||
|
</QInput>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.vn-input-time.q-field--standard.q-field--readonly .q-field__control:before {
|
||||||
|
border-bottom-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vn-input-time.q-field--outlined.q-field--readonly .q-field__control:before {
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Cancel: Cancelar
|
||||||
|
</i18n>
|
|
@ -8,11 +8,13 @@ import toPercentage from './toPercentage';
|
||||||
import toLowerCamel from './toLowerCamel';
|
import toLowerCamel from './toLowerCamel';
|
||||||
import dashIfEmpty from './dashIfEmpty';
|
import dashIfEmpty from './dashIfEmpty';
|
||||||
import dateRange from './dateRange';
|
import dateRange from './dateRange';
|
||||||
|
import toHour from './toHour';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
toLowerCase,
|
toLowerCase,
|
||||||
toLowerCamel,
|
toLowerCamel,
|
||||||
toDate,
|
toDate,
|
||||||
|
toHour,
|
||||||
toDateString,
|
toDateString,
|
||||||
toDateHour,
|
toDateHour,
|
||||||
toRelativeDate,
|
toRelativeDate,
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
export default function isValidDate(date) {
|
||||||
|
return !isNaN(new Date(date).getTime());
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
import isValidDate from 'filters/isValidDate';
|
||||||
|
|
||||||
|
export default function toHour(date) {
|
||||||
|
if (!isValidDate(date)) {
|
||||||
|
return '--:--';
|
||||||
|
}
|
||||||
|
const dateHour = new Date(date);
|
||||||
|
let hours = dateHour.getUTCHours();
|
||||||
|
hours = hours % 12;
|
||||||
|
hours = hours ? hours : 12;
|
||||||
|
|
||||||
|
let minutes = dateHour.getUTCMinutes();
|
||||||
|
minutes = minutes < 10 ? minutes.toString().padStart(2, '0') : minutes;
|
||||||
|
|
||||||
|
return `${hours}:${minutes} ${dateHour.getUTCHours() >= 12 ? 'PM' : 'AM'}`;
|
||||||
|
}
|
|
@ -883,6 +883,10 @@ export default {
|
||||||
pageTitles: {
|
pageTitles: {
|
||||||
routes: 'Routes',
|
routes: 'Routes',
|
||||||
cmrsList: 'External CMRs list',
|
cmrsList: 'External CMRs list',
|
||||||
|
RouteList: 'List',
|
||||||
|
create: 'Create',
|
||||||
|
basicData: 'Basic Data',
|
||||||
|
summary: 'Summary'
|
||||||
},
|
},
|
||||||
cmr: {
|
cmr: {
|
||||||
list: {
|
list: {
|
||||||
|
|
|
@ -883,6 +883,10 @@ export default {
|
||||||
pageTitles: {
|
pageTitles: {
|
||||||
routes: 'Rutas',
|
routes: 'Rutas',
|
||||||
cmrsList: 'Listado de CMRs externos',
|
cmrsList: 'Listado de CMRs externos',
|
||||||
|
RouteList: 'Listado',
|
||||||
|
create: 'Crear',
|
||||||
|
basicData: 'Datos básicos',
|
||||||
|
summary: 'Summary',
|
||||||
},
|
},
|
||||||
cmr: {
|
cmr: {
|
||||||
list: {
|
list: {
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
<script setup>
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
import LeftMenu from 'components/LeftMenu.vue';
|
||||||
|
import RouteDescriptor from 'pages/Route/Card/RouteDescriptor.vue';
|
||||||
|
// import ShelvingDescriptor from 'pages/Shelving/Card/ShelvingDescriptor.vue';
|
||||||
|
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||||
|
<QScrollArea class="fit">
|
||||||
|
<RouteDescriptor />
|
||||||
|
<QSeparator />
|
||||||
|
<LeftMenu source="card" />
|
||||||
|
</QScrollArea>
|
||||||
|
</QDrawer>
|
||||||
|
<QPageContainer>
|
||||||
|
<QPage>
|
||||||
|
<RouterView></RouterView>
|
||||||
|
</QPage>
|
||||||
|
</QPageContainer>
|
||||||
|
</template>
|
|
@ -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>
|
|
@ -0,0 +1,234 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnFilterPanel from 'components/ui/VnFilterPanel.vue';
|
||||||
|
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||||
|
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||||
|
import VnInput from 'components/common/VnInput.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const props = defineProps({
|
||||||
|
dataKey: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['search']);
|
||||||
|
|
||||||
|
const workerList = ref([]);
|
||||||
|
const agencyList = ref([]);
|
||||||
|
const vehicleList = ref([]);
|
||||||
|
const warehouseList = ref([]);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="Workers/search"
|
||||||
|
:filter="{ fields: ['id', 'nickname'] }"
|
||||||
|
sort-by="nickname ASC"
|
||||||
|
limit="30"
|
||||||
|
@on-fetch="(data) => (workerList = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="AgencyModes/isActive"
|
||||||
|
:filter="{ fields: ['id', 'name'] }"
|
||||||
|
sort-by="name ASC"
|
||||||
|
limit="30"
|
||||||
|
@on-fetch="(data) => (agencyList = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="Vehicles"
|
||||||
|
:filter="{ fields: ['id', 'numberPlate'] }"
|
||||||
|
sort-by="numberPlate ASC"
|
||||||
|
limit="30"
|
||||||
|
@on-fetch="(data) => (vehicleList = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData url="Warehouses" @on-fetch="(data) => (warehouseList = data)" auto-load />
|
||||||
|
<VnFilterPanel
|
||||||
|
:data-key="props.dataKey"
|
||||||
|
:search-button="true"
|
||||||
|
@search="emit('search')"
|
||||||
|
>
|
||||||
|
<template #tags="{ tag, formatFn }">
|
||||||
|
<div class="q-gutter-x-xs">
|
||||||
|
<strong>{{ t(`params.${tag.label}`) }}: </strong>
|
||||||
|
<span>{{ formatFn(tag.value) }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #body="{ params }">
|
||||||
|
<QList dense>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection v-if="workerList">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Worker')"
|
||||||
|
v-model="params.workerFk"
|
||||||
|
:options="workerList"
|
||||||
|
option-value="id"
|
||||||
|
option-label="nickname"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
rounded
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
:input-debounce="0"
|
||||||
|
>
|
||||||
|
<template #option="{ itemProps, opt }">
|
||||||
|
<QItem v-bind="itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>{{ opt.name }}</QItemLabel>
|
||||||
|
<QItemLabel caption>
|
||||||
|
{{ opt.nickname }},{{ opt.code }}
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection v-if="agencyList">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Agency')"
|
||||||
|
v-model="params.agencyModeFk"
|
||||||
|
:options="agencyList"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
rounded
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
:input-debounce="0"
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInputDate
|
||||||
|
v-model="params.from"
|
||||||
|
:label="t('From')"
|
||||||
|
is-outlined
|
||||||
|
:disable="Boolean(params.scopeDays)"
|
||||||
|
@update:model-value="params.scopeDays = null"
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInputDate
|
||||||
|
v-model="params.to"
|
||||||
|
:label="t('To')"
|
||||||
|
is-outlined
|
||||||
|
:disable="Boolean(params.scopeDays)"
|
||||||
|
@update:model-value="params.scopeDays = null"
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInput
|
||||||
|
v-model="params.scopeDays"
|
||||||
|
type="number"
|
||||||
|
:label="t('Days Onward')"
|
||||||
|
is-outlined
|
||||||
|
clearable
|
||||||
|
:disable="Boolean(params.from || params.to)"
|
||||||
|
@update:model-value="
|
||||||
|
params.to = null;
|
||||||
|
params.from = null;
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection v-if="vehicleList">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Vehicle')"
|
||||||
|
v-model="params.vehicleFk"
|
||||||
|
:options="vehicleList"
|
||||||
|
option-value="id"
|
||||||
|
option-label="numberPlate"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
rounded
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
:input-debounce="0"
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInput v-model="params.m3" label="m³" is-outlined clearable />
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection v-if="vehicleList">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Warehouse')"
|
||||||
|
v-model="params.warehouseFk"
|
||||||
|
:options="warehouseList"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
rounded
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
:input-debounce="0"
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem class="q-my-sm">
|
||||||
|
<QItemSection>
|
||||||
|
<VnInput
|
||||||
|
v-model="params.description"
|
||||||
|
:label="t('Description')"
|
||||||
|
is-outlined
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</QList>
|
||||||
|
</template>
|
||||||
|
</VnFilterPanel>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
en:
|
||||||
|
params:
|
||||||
|
warehouseFk: Warehouse
|
||||||
|
description: Description
|
||||||
|
m3: m³
|
||||||
|
vehicleFk: Vehicle
|
||||||
|
agencyModeFk: Agency
|
||||||
|
workerFk: Worker
|
||||||
|
from: From
|
||||||
|
to: To
|
||||||
|
es:
|
||||||
|
params:
|
||||||
|
warehouseFk: Almacén
|
||||||
|
description: Descripción
|
||||||
|
m3: m³
|
||||||
|
vehicleFk: Vehículo
|
||||||
|
agencyModeFk: Agencia
|
||||||
|
workerFk: Trabajador
|
||||||
|
from: Desde
|
||||||
|
to: Hasta
|
||||||
|
Warehouse: Almacén
|
||||||
|
Description: Descripción
|
||||||
|
Vehicle: Vehículo
|
||||||
|
Agency: Agencia
|
||||||
|
Worker: Trabajador
|
||||||
|
From: Desde
|
||||||
|
To: Hasta
|
||||||
|
</i18n>
|
|
@ -0,0 +1,214 @@
|
||||||
|
<script setup>
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import FormModel from 'components/FormModel.vue';
|
||||||
|
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
|
||||||
|
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||||
|
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||||
|
import VnInput from 'components/common/VnInput.vue';
|
||||||
|
import axios from 'axios';
|
||||||
|
import VnInputTime from 'components/common/VnInputTime.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const shelvingId = route.params?.id || null;
|
||||||
|
const isNew = Boolean(!shelvingId);
|
||||||
|
const defaultInitialData = {
|
||||||
|
agencyModeFk: null,
|
||||||
|
created: null,
|
||||||
|
description: '',
|
||||||
|
vehicleFk: null,
|
||||||
|
workerFk: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const workerList = ref([]);
|
||||||
|
const agencyList = ref([]);
|
||||||
|
const vehicleList = ref([]);
|
||||||
|
|
||||||
|
const routeFilter = {
|
||||||
|
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 onSave = (data, response) => {
|
||||||
|
if (isNew) {
|
||||||
|
axios.post(`Routes/${response.data?.id}/updateWorkCenter`);
|
||||||
|
router.push({ name: 'RouteSummary', params: { id: response.data?.id } });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<VnSubToolbar />
|
||||||
|
<FetchData
|
||||||
|
url="Workers/search"
|
||||||
|
:filter="{ fields: ['id', 'nickname'] }"
|
||||||
|
sort-by="nickname ASC"
|
||||||
|
limit="30"
|
||||||
|
@on-fetch="(data) => (workerList = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="AgencyModes/isActive"
|
||||||
|
:filter="{ fields: ['id', 'name'] }"
|
||||||
|
sort-by="name ASC"
|
||||||
|
limit="30"
|
||||||
|
@on-fetch="(data) => (agencyList = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="Vehicles"
|
||||||
|
:filter="{ fields: ['id', 'numberPlate'] }"
|
||||||
|
sort-by="numberPlate ASC"
|
||||||
|
limit="30"
|
||||||
|
@on-fetch="(data) => (vehicleList = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FormModel
|
||||||
|
:url="isNew ? null : `Routes/${shelvingId}`"
|
||||||
|
:url-create="isNew ? 'Routes' : null"
|
||||||
|
:observe-form-changes="!isNew"
|
||||||
|
:filter="routeFilter"
|
||||||
|
model="route"
|
||||||
|
:auto-load="!isNew"
|
||||||
|
:form-initial-data="defaultInitialData"
|
||||||
|
@on-data-saved="onSave"
|
||||||
|
>
|
||||||
|
<template #form="{ data }">
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Worker')"
|
||||||
|
v-model="data.workerFk"
|
||||||
|
:options="workerList"
|
||||||
|
option-value="id"
|
||||||
|
option-label="nickname"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
:input-debounce="0"
|
||||||
|
>
|
||||||
|
<template #option="{ itemProps, opt }">
|
||||||
|
<QItem v-bind="itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>{{ opt.name }}</QItemLabel>
|
||||||
|
<QItemLabel caption>
|
||||||
|
{{ opt.nickname }},{{ opt.code }}
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Vehicle')"
|
||||||
|
v-model="data.vehicleFk"
|
||||||
|
:options="vehicleList"
|
||||||
|
option-value="id"
|
||||||
|
option-label="numberPlate"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
:input-debounce="0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Agency')"
|
||||||
|
v-model="data.agencyModeFk"
|
||||||
|
:options="agencyList"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
:input-debounce="0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInputDate v-model="data.created" :label="t('Created')" />
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<template v-if="!isNew">
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
v-model="data.kmStart"
|
||||||
|
:label="t('Km Start')"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInput v-model="data.kmEnd" :label="t('Km End')" clearable />
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInputTime
|
||||||
|
v-model="data.started"
|
||||||
|
:label="t('Hour started')"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInputTime
|
||||||
|
v-model="data.finished"
|
||||||
|
:label="t('Hour finished')"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
v-model="data.description"
|
||||||
|
:label="t('Description')"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
</FormModel>
|
||||||
|
</template>
|
|
@ -0,0 +1,20 @@
|
||||||
|
<script setup>
|
||||||
|
import VnSearchbar from 'components/ui/VnSearchbar.vue';
|
||||||
|
import {useI18n} from "vue-i18n";
|
||||||
|
const { t } = useI18n();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<VnSearchbar
|
||||||
|
data-key="RouteList"
|
||||||
|
:label="t('Search route')"
|
||||||
|
:info="t('You can search by route reference')"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Search route: Buscar rutas
|
||||||
|
You can search by route reference: Puedes buscar por referencia de la ruta
|
||||||
|
</i18n>
|
|
@ -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>
|
|
@ -0,0 +1,530 @@
|
||||||
|
<script setup>
|
||||||
|
import VnPaginate from 'components/ui/VnPaginate.vue';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||||
|
import { dashIfEmpty, toDate, toHour } from 'src/filters';
|
||||||
|
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import { useValidator } from 'composables/useValidator';
|
||||||
|
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||||
|
import VnInput from 'components/common/VnInput.vue';
|
||||||
|
import VnInputTime from 'components/common/VnInputTime.vue';
|
||||||
|
import axios from 'axios';
|
||||||
|
import RouteSearchbar from 'pages/Route/Card/RouteSearchbar.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 { t } = useI18n();
|
||||||
|
const { validate } = useValidator();
|
||||||
|
const quasar = useQuasar();
|
||||||
|
|
||||||
|
const to = Date.vnNew();
|
||||||
|
to.setDate(to.getDate() + 1);
|
||||||
|
to.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
const from = Date.vnNew();
|
||||||
|
from.setDate(from.getDate());
|
||||||
|
from.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
const params = ref({ from, to });
|
||||||
|
|
||||||
|
onMounted(() => (stateStore.rightDrawer = true));
|
||||||
|
onUnmounted(() => (stateStore.rightDrawer = false));
|
||||||
|
|
||||||
|
const selectedRows = ref([]);
|
||||||
|
const columns = computed(() => [
|
||||||
|
{
|
||||||
|
name: 'ID',
|
||||||
|
label: t('ID'),
|
||||||
|
field: (row) => row.id,
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'worker',
|
||||||
|
label: t('Worker'),
|
||||||
|
field: (row) => row.workerUserName,
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'agency',
|
||||||
|
label: t('Agency'),
|
||||||
|
field: (row) => row.agencyName,
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'vehicle',
|
||||||
|
label: t('Vehicle'),
|
||||||
|
field: (row) => row.vehiclePlateNumber,
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'date',
|
||||||
|
label: t('Date'),
|
||||||
|
field: (row) => row.created,
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'volume',
|
||||||
|
label: 'm³',
|
||||||
|
field: (row) => dashIfEmpty(row.m3),
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'description',
|
||||||
|
label: t('Description'),
|
||||||
|
field: (row) => row.description,
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'started',
|
||||||
|
label: t('Hour started'),
|
||||||
|
field: (row) => toHour(row.started),
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'finished',
|
||||||
|
label: t('Hour finished'),
|
||||||
|
field: (row) => toHour(row.finished),
|
||||||
|
sortable: true,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'actions',
|
||||||
|
label: '',
|
||||||
|
sortable: false,
|
||||||
|
align: 'right',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const refreshKey = ref(0);
|
||||||
|
const workers = ref([]);
|
||||||
|
const agencyList = ref([]);
|
||||||
|
const vehicleList = ref([]);
|
||||||
|
const updateRoute = async (route) => {
|
||||||
|
try {
|
||||||
|
return await axios.patch(`Routes/${route.id}`, route);
|
||||||
|
} catch (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateVehicle = (row, vehicle) => {
|
||||||
|
row.vehicleFk = vehicle.id;
|
||||||
|
row.vehiclePlateNumber = vehicle.numberPlate;
|
||||||
|
updateRoute(row);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateAgency = (row, agency) => {
|
||||||
|
row.agencyModeFk = agency.id;
|
||||||
|
row.agencyName = agency.name;
|
||||||
|
updateRoute(row);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateWorker = (row, worker) => {
|
||||||
|
row.workerFk = worker.id;
|
||||||
|
row.workerUserName = worker.name;
|
||||||
|
updateRoute(row);
|
||||||
|
};
|
||||||
|
|
||||||
|
const confirmationDialog = ref(false);
|
||||||
|
const startingDate = ref(null);
|
||||||
|
|
||||||
|
const cloneRoutes = () => {
|
||||||
|
axios.post('Routes/clone', {
|
||||||
|
created: startingDate.value,
|
||||||
|
ids: selectedRows.value.map((row) => row?.id),
|
||||||
|
});
|
||||||
|
refreshKey.value++;
|
||||||
|
startingDate.value = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const markAsServed = () => {
|
||||||
|
selectedRows.value.forEach((row) => {
|
||||||
|
if (row?.id) {
|
||||||
|
axios.patch(`Routes/${row?.id}`, { isOk: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
refreshKey.value++;
|
||||||
|
startingDate.value = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
function previewRoute(id) {
|
||||||
|
if (!id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
quasar.dialog({
|
||||||
|
component: RouteSummaryDialog,
|
||||||
|
componentProps: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<template v-if="stateStore.isHeaderMounted()">
|
||||||
|
<Teleport to="#searchbar">
|
||||||
|
<RouteSearchbar />
|
||||||
|
</Teleport>
|
||||||
|
<Teleport to="#actions-append">
|
||||||
|
<div class="row q-gutter-x-sm">
|
||||||
|
<QBtn
|
||||||
|
flat
|
||||||
|
@click="stateStore.toggleRightDrawer()"
|
||||||
|
round
|
||||||
|
dense
|
||||||
|
icon="menu"
|
||||||
|
>
|
||||||
|
<QTooltip bottom anchor="bottom right">
|
||||||
|
{{ t('globals.collapseMenu') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
</div>
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
|
<QDialog v-model="confirmationDialog">
|
||||||
|
<QCard style="min-width: 350px">
|
||||||
|
<QCardSection>
|
||||||
|
<p class="text-h6 q-ma-none">{{ t('Select the starting date') }}</p>
|
||||||
|
</QCardSection>
|
||||||
|
|
||||||
|
<QCardSection class="q-pt-none">
|
||||||
|
<VnInputDate
|
||||||
|
:label="t('Stating date')"
|
||||||
|
v-model="startingDate"
|
||||||
|
autofocus
|
||||||
|
/>
|
||||||
|
</QCardSection>
|
||||||
|
<!-- TODO: Add report -->
|
||||||
|
<QCardActions align="right">
|
||||||
|
<QBtn flat :label="t('Cancel')" v-close-popup class="text-primary" />
|
||||||
|
<QBtn color="primary" v-close-popup @click="cloneRoutes">
|
||||||
|
{{ t('Clone') }}
|
||||||
|
</QBtn>
|
||||||
|
</QCardActions>
|
||||||
|
</QCard>
|
||||||
|
</QDialog>
|
||||||
|
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||||
|
<QScrollArea class="fit text-grey-8">
|
||||||
|
<RouteFilter data-key="RouteList" />
|
||||||
|
</QScrollArea>
|
||||||
|
</QDrawer>
|
||||||
|
<FetchData
|
||||||
|
url="Workers/activeWithInheritedRole"
|
||||||
|
@on-fetch="(data) => (workers = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData url="AgencyModes" @on-fetch="(data) => (agencyList = data)" auto-load />
|
||||||
|
<FetchData url="Vehicles" @on-fetch="(data) => (vehicleList = data)" auto-load />
|
||||||
|
<QPage class="column items-center">
|
||||||
|
<QToolbar class="bg-vn-dark justify-end">
|
||||||
|
<div id="st-actions" class="q-pa-sm">
|
||||||
|
<QBtn
|
||||||
|
icon="vn:clone"
|
||||||
|
color="primary"
|
||||||
|
class="q-mr-sm"
|
||||||
|
:disable="!selectedRows?.length"
|
||||||
|
@click="confirmationDialog = true"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('Clone Selected Routes') }}</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
|
||||||
|
<QBtn
|
||||||
|
icon="check"
|
||||||
|
color="primary"
|
||||||
|
class="q-mr-sm"
|
||||||
|
:disable="!selectedRows?.length"
|
||||||
|
@click="markAsServed"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('Mark as served') }}</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
</div>
|
||||||
|
</QToolbar>
|
||||||
|
<div class="route-list">
|
||||||
|
<VnPaginate
|
||||||
|
:key="refreshKey"
|
||||||
|
data-key="RouteList"
|
||||||
|
url="Routes/filter"
|
||||||
|
:order="['created DESC', 'id DESC']"
|
||||||
|
:limit="20"
|
||||||
|
auto-load
|
||||||
|
>
|
||||||
|
<template #body="{ rows }">
|
||||||
|
<div class="q-pa-md">
|
||||||
|
<QTable
|
||||||
|
v-model:selected="selectedRows"
|
||||||
|
:columns="columns"
|
||||||
|
:rows="rows"
|
||||||
|
flat
|
||||||
|
row-key="id"
|
||||||
|
selection="multiple"
|
||||||
|
:rows-per-page-options="[0]"
|
||||||
|
hide-pagination
|
||||||
|
>
|
||||||
|
<template #body-cell-worker="props">
|
||||||
|
<QTd :props="props">
|
||||||
|
{{ props.row?.workerUserName }}
|
||||||
|
<QPopupEdit
|
||||||
|
:model-value="props.row.workerFk"
|
||||||
|
v-slot="scope"
|
||||||
|
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 }">
|
||||||
|
<QItem
|
||||||
|
v-bind="itemProps"
|
||||||
|
class="q-pa-xs row items-center"
|
||||||
|
>
|
||||||
|
<QItemSection
|
||||||
|
class="col-9 justify-center"
|
||||||
|
>
|
||||||
|
<span>{{ opt.name }}</span>
|
||||||
|
<span class="text-grey">{{
|
||||||
|
opt.nickname
|
||||||
|
}}</span>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelectFilter>
|
||||||
|
</QPopupEdit>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-agency="props">
|
||||||
|
<QTd :props="props">
|
||||||
|
{{ props.row?.agencyName }}
|
||||||
|
<QPopupEdit
|
||||||
|
:model-value="props.row.agencyModeFk"
|
||||||
|
v-slot="scope"
|
||||||
|
buttons
|
||||||
|
@update:model-value="
|
||||||
|
(agency) => updateAgency(props.row, agency)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Agency')"
|
||||||
|
v-model="scope.value"
|
||||||
|
:options="agencyList"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
hide-selected
|
||||||
|
autofocus
|
||||||
|
:emit-value="false"
|
||||||
|
:rules="validate('route.agencyFk')"
|
||||||
|
:is-clearable="false"
|
||||||
|
@keyup.enter="scope.set"
|
||||||
|
@focus="($event) => $event.target.select()"
|
||||||
|
/>
|
||||||
|
</QPopupEdit>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-vehicle="props">
|
||||||
|
<QTd :props="props">
|
||||||
|
{{ props.row?.vehiclePlateNumber }}
|
||||||
|
<QPopupEdit
|
||||||
|
:model-value="props.row.vehicleFk"
|
||||||
|
v-slot="scope"
|
||||||
|
buttons
|
||||||
|
@update:model-value="
|
||||||
|
(vehicle) => updateVehicle(props.row, vehicle)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<VnSelectFilter
|
||||||
|
:label="t('Vehicle')"
|
||||||
|
v-model="scope.value"
|
||||||
|
:options="vehicleList"
|
||||||
|
option-value="id"
|
||||||
|
option-label="numberPlate"
|
||||||
|
hide-selected
|
||||||
|
autofocus
|
||||||
|
:emit-value="false"
|
||||||
|
:rules="validate('route.vehicleFk')"
|
||||||
|
:is-clearable="false"
|
||||||
|
@keyup.enter="scope.set"
|
||||||
|
@focus="($event) => $event.target.select()"
|
||||||
|
/>
|
||||||
|
</QPopupEdit>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-date="props">
|
||||||
|
<QTd :props="props">
|
||||||
|
{{ toDate(props.row?.created) }}
|
||||||
|
<QPopupEdit
|
||||||
|
v-model="props.row.created"
|
||||||
|
v-slot="scope"
|
||||||
|
@update:model-value="updateRoute(props.row)"
|
||||||
|
buttons
|
||||||
|
>
|
||||||
|
<VnInputDate
|
||||||
|
v-model="scope.value"
|
||||||
|
autofocus
|
||||||
|
:label="t('Date')"
|
||||||
|
:rules="validate('route.created')"
|
||||||
|
:is-clearable="false"
|
||||||
|
@keyup.enter="scope.set"
|
||||||
|
@focus="($event) => $event.target.select()"
|
||||||
|
/>
|
||||||
|
</QPopupEdit>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-description="props">
|
||||||
|
<QTd :props="props">
|
||||||
|
{{ props.row?.description }}
|
||||||
|
<QPopupEdit
|
||||||
|
v-model="props.row.description"
|
||||||
|
v-slot="scope"
|
||||||
|
@update:model-value="updateRoute(props.row)"
|
||||||
|
buttons
|
||||||
|
>
|
||||||
|
<VnInput
|
||||||
|
v-model="scope.value"
|
||||||
|
autofocus
|
||||||
|
:label="t('Description')"
|
||||||
|
:rules="validate('route.description')"
|
||||||
|
:is-clearable="false"
|
||||||
|
@keyup.enter="scope.set"
|
||||||
|
@focus="($event) => $event.target.select()"
|
||||||
|
/>
|
||||||
|
</QPopupEdit>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-started="props">
|
||||||
|
<QTd :props="props">
|
||||||
|
{{ toHour(props.row.started) }}
|
||||||
|
<QPopupEdit
|
||||||
|
v-model="props.row.started"
|
||||||
|
v-slot="scope"
|
||||||
|
buttons
|
||||||
|
@update:model-value="updateRoute(props.row)"
|
||||||
|
>
|
||||||
|
<VnInputTime
|
||||||
|
v-model="scope.value"
|
||||||
|
autofocus
|
||||||
|
:label="t('Hour started')"
|
||||||
|
:rules="validate('route.started')"
|
||||||
|
:is-clearable="false"
|
||||||
|
@keyup.enter="scope.set"
|
||||||
|
@focus="($event) => $event.target.select()"
|
||||||
|
/>
|
||||||
|
</QPopupEdit>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-finished="props">
|
||||||
|
<QTd :props="props">
|
||||||
|
{{ toHour(props.row.finished) }}
|
||||||
|
<QPopupEdit
|
||||||
|
v-model="props.row.finished"
|
||||||
|
v-slot="scope"
|
||||||
|
buttons
|
||||||
|
@update:model-value="updateRoute(props.row)"
|
||||||
|
>
|
||||||
|
<VnInputTime
|
||||||
|
v-model="scope.value"
|
||||||
|
autofocus
|
||||||
|
:label="t('Hour finished')"
|
||||||
|
:rules="validate('route.finished')"
|
||||||
|
:is-clearable="false"
|
||||||
|
@keyup.enter="scope.set"
|
||||||
|
@focus="($event) => $event.target.select()"
|
||||||
|
/>
|
||||||
|
</QPopupEdit>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<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]">
|
||||||
|
<RouterLink :to="{ name: 'RouteCreate' }">
|
||||||
|
<QBtn fab icon="add" color="primary" />
|
||||||
|
<QTooltip>
|
||||||
|
{{ t('newRoute') }}
|
||||||
|
</QTooltip>
|
||||||
|
</RouterLink>
|
||||||
|
</QPageSticky>
|
||||||
|
</QPage>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.route-list {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
i {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<i18n>
|
||||||
|
en:
|
||||||
|
newRoute: New Route
|
||||||
|
es:
|
||||||
|
ID: ID
|
||||||
|
Worker: Trabajador
|
||||||
|
Agency: Agencia
|
||||||
|
Vehicle: Vehículo
|
||||||
|
Date: Fecha
|
||||||
|
Description: Descripción
|
||||||
|
Hour started: Hora inicio
|
||||||
|
Hour finished: Hora fin
|
||||||
|
newRoute: Nueva Ruta
|
||||||
|
Clone Selected Routes: Clonar rutas seleccionadas
|
||||||
|
Select the starting date: Seleccione la fecha de inicio
|
||||||
|
Stating date: Fecha de inicio
|
||||||
|
Cancel: Cancelar
|
||||||
|
Clone: Clonar
|
||||||
|
Mark as served: Marcar como servidas
|
||||||
|
</i18n>
|
|
@ -10,15 +10,15 @@ export default {
|
||||||
component: RouterView,
|
component: RouterView,
|
||||||
redirect: { name: 'RouteMain' },
|
redirect: { name: 'RouteMain' },
|
||||||
menus: {
|
menus: {
|
||||||
main: ['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',
|
||||||
|
@ -29,6 +29,49 @@ export default {
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/Route/Cmr/CmrList.vue'),
|
component: () => import('src/pages/Route/Cmr/CmrList.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'list',
|
||||||
|
name: 'RouteList',
|
||||||
|
meta: {
|
||||||
|
title: 'RouteList',
|
||||||
|
icon: 'view_list',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Route/RouteList.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'create',
|
||||||
|
name: 'RouteCreate',
|
||||||
|
meta: {
|
||||||
|
title: 'create',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Route/Card/RouteForm.vue'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'RouteCard',
|
||||||
|
path: ':id',
|
||||||
|
component: () => import('src/pages/Route/Card/RouteCard.vue'),
|
||||||
|
redirect: { name: 'RouteSummary' },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'RouteBasicData',
|
||||||
|
path: 'basic-data',
|
||||||
|
meta: {
|
||||||
|
title: 'basicData',
|
||||||
|
icon: 'vn:settings',
|
||||||
|
},
|
||||||
|
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