forked from verdnatura/salix-front
Create order catalog section
This commit is contained in:
parent
42c8e8c80f
commit
a65db2f4cf
|
@ -555,6 +555,7 @@ export default {
|
||||||
create: 'Create',
|
create: 'Create',
|
||||||
summary: 'Summary',
|
summary: 'Summary',
|
||||||
basicData: 'Basic Data',
|
basicData: 'Basic Data',
|
||||||
|
catalog: 'Catalog',
|
||||||
},
|
},
|
||||||
field: {
|
field: {
|
||||||
salesPersonFk: 'Sales Person',
|
salesPersonFk: 'Sales Person',
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import LeftMenu from 'components/LeftMenu.vue';
|
import LeftMenu from 'components/LeftMenu.vue';
|
||||||
import { useStateStore } from 'stores/useStateStore';
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
import OrderSearchbar from 'pages/Order/Card/OrderSearchbar.vue';
|
import OrderDescriptor from 'pages/Order/Card/OrderDescriptor.vue';
|
||||||
import OrderDescriptor from "pages/Order/Card/OrderDescriptor.vue";
|
|
||||||
|
|
||||||
const stateStore = useStateStore();
|
const stateStore = useStateStore();
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()">
|
|
||||||
<OrderSearchbar />
|
|
||||||
</Teleport>
|
|
||||||
<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">
|
||||||
<OrderDescriptor />
|
<OrderDescriptor />
|
||||||
|
|
|
@ -0,0 +1,182 @@
|
||||||
|
<script setup>
|
||||||
|
import { useSession } from 'composables/useSession';
|
||||||
|
import VnLv from 'components/ui/VnLv.vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import OrderCatalogItemDialog from 'pages/Order/Card/OrderCatalogItemDialog.vue';
|
||||||
|
import toCurrency from '../../../filters/toCurrency';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
const session = useSession();
|
||||||
|
const token = session.getToken();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
item: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const dialog = ref(null);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="container order-catalog-item overflow-hidden">
|
||||||
|
<div class="card shadow-6 bg-dark">
|
||||||
|
<div class="img-wrapper">
|
||||||
|
<QImg
|
||||||
|
:src="`/api/Images/catalog/200x200/${item.id}/download?access_token=${token}`"
|
||||||
|
spinner-color="primary"
|
||||||
|
:ratio="1"
|
||||||
|
height="192"
|
||||||
|
width="192"
|
||||||
|
class="image"
|
||||||
|
/>
|
||||||
|
<div v-if="item.hex" class="item-color-container">
|
||||||
|
<div
|
||||||
|
class="item-color"
|
||||||
|
:style="{ backgroundColor: `#${item.hex}` }"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<span class="link">{{ item.name }}</span>
|
||||||
|
<p class="subName">{{ item.subName }}</p>
|
||||||
|
<template v-for="index in 4" :key="`tag-${index}`">
|
||||||
|
<VnLv
|
||||||
|
v-if="item?.[`tag${index + 4}`]"
|
||||||
|
:label="item?.[`tag${index + 4}`] + ':'"
|
||||||
|
:value="item?.[`value${index + 4}`]"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<QRating
|
||||||
|
:model-value="item.stars"
|
||||||
|
icon="star"
|
||||||
|
icon-selected="star"
|
||||||
|
color="primary"
|
||||||
|
readonly
|
||||||
|
/>
|
||||||
|
<div class="footer">
|
||||||
|
<div class="price">
|
||||||
|
<p>{{ item.available }} to {{ item.price }}</p>
|
||||||
|
<QIcon name="add_circle" class="icon">
|
||||||
|
<QTooltip>{{ t('globals.add') }}</QTooltip>
|
||||||
|
<QPopupProxy ref="dialog">
|
||||||
|
<OrderCatalogItemDialog
|
||||||
|
:prices="item.prices"
|
||||||
|
@added="() => dialog.hide()"
|
||||||
|
/>
|
||||||
|
</QPopupProxy>
|
||||||
|
</QIcon>
|
||||||
|
</div>
|
||||||
|
<p v-if="item.priceKg" class="price-kg">
|
||||||
|
{{ t('price-kg') }} {{ toCurrency(item.priceKg) || 1123 }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.order-catalog-item {
|
||||||
|
.vn-label-value {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
font-size: 11px;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
color: var(--vn-label);
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
color: var(--vn-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.container {
|
||||||
|
max-width: 448px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
max-height: 192px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card > * {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.img-wrapper {
|
||||||
|
position: relative;
|
||||||
|
max-width: 192px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
padding: 12px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.subName {
|
||||||
|
color: var(--vn-label);
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
.price {
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
color: $primary;
|
||||||
|
font-size: 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-kg {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-color-container {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 12px;
|
||||||
|
right: 12px;
|
||||||
|
background: linear-gradient($dark, $primary);
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
padding: 4px;
|
||||||
|
|
||||||
|
.item-color {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
price-kg: Precio por Kg
|
||||||
|
en:
|
||||||
|
price-kg: Price per Kg
|
||||||
|
</i18n>
|
|
@ -0,0 +1,83 @@
|
||||||
|
<script setup>
|
||||||
|
import toCurrency from '../../../filters/toCurrency';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import useNotify from 'composables/useNotify';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
const { notify } = useNotify();
|
||||||
|
const props = defineProps({
|
||||||
|
prices: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['added']);
|
||||||
|
|
||||||
|
const fields = ref((props.prices || []).map((item) => ({ ...item, quantity: 0 })));
|
||||||
|
|
||||||
|
const addToOrder = async () => {
|
||||||
|
const items = (fields.value || []).filter((item) => Number(item.quantity) > 0);
|
||||||
|
await axios.post('/OrderRows/addToOrder', {
|
||||||
|
items,
|
||||||
|
orderFk: Number(route.params.id),
|
||||||
|
});
|
||||||
|
notify(t('globals.dataSaved'), 'positive');
|
||||||
|
emit('added');
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="container order-catalog-item q-pb-md">
|
||||||
|
<QForm @submit.prevent="addToOrder">
|
||||||
|
<QMarkupTable class="shadow-0">
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="item in fields" :key="item.warehouse">
|
||||||
|
<td class="text-bold q-py-lg">
|
||||||
|
{{ item.warehouse }}
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<span
|
||||||
|
class="link"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
item.quantity =
|
||||||
|
Number(item.quantity) + item.grouping;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ item.grouping }}
|
||||||
|
</span>
|
||||||
|
x {{ toCurrency(item.price) }}
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<QInput
|
||||||
|
v-model="item.quantity"
|
||||||
|
type="number"
|
||||||
|
:step="item.grouping"
|
||||||
|
min="0"
|
||||||
|
dense
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</QMarkupTable>
|
||||||
|
<div class="flex justify-center q-mt-lg">
|
||||||
|
<QBtn color="primary" type="submit">
|
||||||
|
{{ t('globals.add') }}
|
||||||
|
</QBtn>
|
||||||
|
</div>
|
||||||
|
</QForm>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.container {
|
||||||
|
max-width: 448px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -6,9 +6,12 @@ import { useI18n } from 'vue-i18n';
|
||||||
import VnLv from 'components/ui/VnLv.vue';
|
import VnLv from 'components/ui/VnLv.vue';
|
||||||
import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
|
import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
|
||||||
import { toCurrency, toDateHour } from 'src/filters';
|
import { toCurrency, toDateHour } from 'src/filters';
|
||||||
|
import OrderSearchbar from 'pages/Order/Card/OrderSearchbar.vue';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
|
||||||
const $props = defineProps({
|
const $props = defineProps({
|
||||||
id: {
|
id: {
|
||||||
|
@ -44,11 +47,14 @@ const detailsColumns = ref([
|
||||||
name: 'amount',
|
name: 'amount',
|
||||||
label: t('claim.summary.description'),
|
label: t('claim.summary.description'),
|
||||||
field: (row) => toCurrency(row?.quantity * row?.price),
|
field: (row) => toCurrency(row?.quantity * row?.price),
|
||||||
}
|
},
|
||||||
])
|
]);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()">
|
||||||
|
<OrderSearchbar />
|
||||||
|
</Teleport>
|
||||||
<CardSummary ref="summary" :url="`Orders/${entityId}/summary`">
|
<CardSummary ref="summary" :url="`Orders/${entityId}/summary`">
|
||||||
<template #header="{ entity }">
|
<template #header="{ entity }">
|
||||||
{{ t('order.summary.basket') }} #{{ entity?.id }} -
|
{{ t('order.summary.basket') }} #{{ entity?.id }} -
|
||||||
|
@ -147,8 +153,12 @@ const detailsColumns = ref([
|
||||||
<p class="header">
|
<p class="header">
|
||||||
{{ t('order.summary.details') }}
|
{{ t('order.summary.details') }}
|
||||||
</p>
|
</p>
|
||||||
<QTable :columns="detailsColumns" :rows="entity?.rows" flat hide-pagination>
|
<QTable
|
||||||
|
:columns="detailsColumns"
|
||||||
|
:rows="entity?.rows"
|
||||||
|
flat
|
||||||
|
hide-pagination
|
||||||
|
>
|
||||||
</QTable>
|
</QTable>
|
||||||
</QCard>
|
</QCard>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
<script setup>
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { onMounted, onUnmounted } from 'vue';
|
||||||
|
import VnPaginate from 'components/ui/VnPaginate.vue';
|
||||||
|
import OrderCatalogItem from 'pages/Order/Card/OrderCatalogItem.vue';
|
||||||
|
import OrderSearchbar from 'pages/Order/Card/OrderSearchbar.vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
onMounted(() => (stateStore.rightDrawer = true));
|
||||||
|
onUnmounted(() => (stateStore.rightDrawer = false));
|
||||||
|
|
||||||
|
const catalogParams = {
|
||||||
|
orderFk: route.params.id,
|
||||||
|
orderBy: JSON.stringify({ field: 'relevancy DESC, name', way: 'ASC', isTag: false }),
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()">
|
||||||
|
<OrderSearchbar
|
||||||
|
data-key="OrderCatalogList"
|
||||||
|
url="Orders/CatalogFilter"
|
||||||
|
:limit="50"
|
||||||
|
:user-params="catalogParams"
|
||||||
|
/>
|
||||||
|
</Teleport>
|
||||||
|
<Teleport v-if="stateStore.isHeaderMounted()" to="#actions-append">
|
||||||
|
<div class="row q-gutter-x-sm">
|
||||||
|
<QBtn
|
||||||
|
flat
|
||||||
|
@click.stop="stateStore.toggleRightDrawer()"
|
||||||
|
round
|
||||||
|
dense
|
||||||
|
icon="menu"
|
||||||
|
>
|
||||||
|
<QTooltip bottom anchor="bottom right">
|
||||||
|
{{ t('globals.collapseMenu') }}
|
||||||
|
</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
</div>
|
||||||
|
</Teleport>
|
||||||
|
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||||
|
<QScrollArea class="fit text-grey-8"></QScrollArea>
|
||||||
|
</QDrawer>
|
||||||
|
<QPage class="column items-center q-pa-md">
|
||||||
|
<div class="card-list">
|
||||||
|
<VnPaginate
|
||||||
|
data-key="OrderCatalogList"
|
||||||
|
url="Orders/CatalogFilter"
|
||||||
|
:limit="50"
|
||||||
|
:user-params="catalogParams"
|
||||||
|
auto-load
|
||||||
|
>
|
||||||
|
<template #body="{ rows }">
|
||||||
|
<div class="catalog-list">
|
||||||
|
<OrderCatalogItem v-for="row in rows" :key="row.id" :item="row" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</VnPaginate>
|
||||||
|
</div>
|
||||||
|
</QPage>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.card-list {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.catalog-list {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -11,7 +11,7 @@ export default {
|
||||||
redirect: { name: 'OrderMain' },
|
redirect: { name: 'OrderMain' },
|
||||||
menus: {
|
menus: {
|
||||||
main: ['OrderList'],
|
main: ['OrderList'],
|
||||||
card: ['OrderBasicData'],
|
card: ['OrderBasicData', 'OrderCatalog'],
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
|
@ -63,6 +63,15 @@ export default {
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/Order/Card/OrderForm.vue'),
|
component: () => import('src/pages/Order/Card/OrderForm.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'OrderCatalog',
|
||||||
|
path: 'catalog',
|
||||||
|
meta: {
|
||||||
|
title: 'catalog',
|
||||||
|
icon: 'vn:basket',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Order/OrderCatalog.vue'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
Loading…
Reference in New Issue