refactor: refs #6897 entryBuyList use vnTable

This commit is contained in:
Pablo Natek 2024-10-08 12:33:48 +02:00
parent 629524d63f
commit 70decb68ea
7 changed files with 150 additions and 469 deletions

View File

@ -43,7 +43,7 @@ const enterEvent = {
const defaultAttrs = { const defaultAttrs = {
filled: !$props.showTitle, filled: !$props.showTitle,
class: 'q-px-xs q-pb-xs q-pt-none fit', // class: 'q-px-xs q-pb-xs q-pt-none fit',
dense: true, dense: true,
}; };
@ -106,9 +106,9 @@ const components = {
component: markRaw(QCheckbox), component: markRaw(QCheckbox),
event: updateEvent, event: updateEvent,
attrs: { attrs: {
dense: true, class: $props.showTitle ? 'q-py-sm' : 'q-px-md q-py-xs fit',
class: $props.showTitle ? 'q-py-sm q-mt-md' : 'q-px-md q-py-xs fit',
'toggle-indeterminate': true, 'toggle-indeterminate': true,
size: 'sm',
}, },
forceAttrs, forceAttrs,
}, },

View File

@ -53,9 +53,9 @@ defineExpose({ orderBy });
@click="orderBy(name, model?.direction)" @click="orderBy(name, model?.direction)"
class="row items-center no-wrap cursor-pointer" class="row items-center no-wrap cursor-pointer"
> >
<span :title="label">{{ label }}</span> <span :title="label" class="title">{{ label }}</span>
<QChip <QChip
v-if="name" v-if="name && model?.index"
:label="!vertical ? model?.index : ''" :label="!vertical ? model?.index : ''"
:icon=" :icon="
(model?.index || hover) && !vertical (model?.index || hover) && !vertical
@ -71,7 +71,7 @@ defineExpose({ orderBy });
]" ]"
class="no-box-shadow" class="no-box-shadow"
:clickable="true" :clickable="true"
style="min-width: 40px" style="min-width: 40px; max-height: 30px"
> >
<div <div
class="column flex-center" class="column flex-center"
@ -93,3 +93,13 @@ defineExpose({ orderBy });
</QChip> </QChip>
</div> </div>
</template> </template>
<style lang="scss" scoped>
.title {
display: flex;
justify-content: center;
align-items: center;
height: 30px;
width: 100%;
color: var(--vn-label-color);
}
</style>

View File

@ -451,13 +451,12 @@ function handleOnDataSaved(_, res) {
/> />
</template> </template>
<template #header-cell="{ col }"> <template #header-cell="{ col }">
<QTh v-if="col.visible ?? true"> <QTh v-if="col.visible ?? true" class="q-px-xl">
<div <div
class="column self-start q-ml-xs ellipsis" class="q-pa-xs"
:class="`text-${col?.align ?? 'left'}`"
:style="$props.columnSearch ? 'height: 75px' : ''" :style="$props.columnSearch ? 'height: 75px' : ''"
> >
<div class="row items-center no-wrap" style="height: 30px"> <div class="text-center" style="height: 30px">
<QTooltip v-if="col.toolTip">{{ col.toolTip }}</QTooltip> <QTooltip v-if="col.toolTip">{{ col.toolTip }}</QTooltip>
<VnTableOrder <VnTableOrder
v-model="orders[col.orderBy ?? col.name]" v-model="orders[col.orderBy ?? col.name]"
@ -821,21 +820,6 @@ es:
top: 0; top: 0;
padding: 12px 0; padding: 12px 0;
} }
tbody {
.q-checkbox {
display: flex;
margin-bottom: 9px;
& .q-checkbox__label {
margin-left: 31px;
color: var(--vn-text-color);
}
& .q-checkbox__inner {
position: absolute;
left: 0;
color: var(--vn-label-color);
}
}
}
.sticky { .sticky {
position: sticky; position: sticky;
right: 0; right: 0;

View File

@ -57,7 +57,7 @@ const tags = computed(() => {
.inline-tag { .inline-tag {
height: 1rem; height: 1rem;
margin: 0.05rem; margin: 0.05rem;
color: $color-font-secondary; color: var(--vn-label-color);
text-align: center; text-align: center;
font-size: smaller; font-size: smaller;
padding: 1px; padding: 1px;

View File

@ -240,7 +240,6 @@ input::-webkit-inner-spin-button {
.q-table { .q-table {
th, th,
td { td {
padding: 1px 10px 1px 10px;
max-width: 100px; max-width: 100px;
div span { div span {
overflow: hidden; overflow: hidden;

View File

@ -189,7 +189,7 @@ const sendCampaignMetricsEmail = ({ address }) => {
<div v-if="row.subName" class="subName"> <div v-if="row.subName" class="subName">
{{ row.subName }} {{ row.subName }}
</div> </div>
<FetchedTags :item="row" :max-length="3" /> <FetchedTags :item="row" />
</template> </template>
<template #moreFilterPanel="{ params }"> <template #moreFilterPanel="{ params }">
<div class="column no-wrap flex-center q-gutter-y-md q-mt-xs q-pr-xl"> <div class="column no-wrap flex-center q-gutter-y-md q-mt-xs q-pr-xl">

View File

@ -1,472 +1,160 @@
<script setup> <script setup>
import { ref, computed } from 'vue'; import { useStateStore } from 'stores/useStateStore';
import { useRoute, useRouter } from 'vue-router'; import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { QBtn } from 'quasar'; import { onMounted } from 'vue';
import VnPaginate from 'src/components/ui/VnPaginate.vue';
import FetchData from 'src/components/FetchData.vue';
import VnSelect from 'components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue';
import FetchedTags from 'components/ui/FetchedTags.vue';
import VnConfirm from 'components/ui/VnConfirm.vue';
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import VnTable from 'src/components/VnTable/VnTable.vue';
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
import FetchedTags from 'components/ui/FetchedTags.vue';
import { useQuasar } from 'quasar'; const stateStore = useStateStore();
import { toCurrency } from 'src/filters';
import axios from 'axios';
import useNotify from 'src/composables/useNotify.js';
const quasar = useQuasar();
const route = useRoute(); const route = useRoute();
const router = useRouter();
const { t } = useI18n(); const { t } = useI18n();
const { notify } = useNotify(); const columns = [
{
const rowsSelected = ref([]); label: 'Nv',
const entryBuysPaginateRef = ref(null); name: 'isIgnored',
const originalRowDataCopy = ref(null); component: 'checkbox',
const getInputEvents = (colField, props) => {
return colField === 'packagingFk'
? { 'update:modelValue': () => saveChange(colField, props) }
: {
'keyup.enter': () => saveChange(colField, props),
blur: () => saveChange(colField, props),
};
};
const tableColumnComponents = computed(() => ({
item: {
component: QBtn,
props: {
color: 'primary',
flat: true,
},
event: () => ({}),
}, },
quantity: { {
component: VnInput, label: 'Id',
props: { name: 'itemFk',
type: 'number', component: 'input',
min: 0, create: true,
class: 'input-number', inputStyle: 'text-align: right',
dense: true,
},
event: getInputEvents,
}, },
packagingFk: { {
component: VnSelect, label: t('Article'),
props: { name: 'name',
'option-value': 'id',
'option-label': 'id',
'emit-value': true,
'map-options': true,
'use-input': true,
'hide-selected': true,
url: 'Packagings',
fields: ['id'],
where: { freightItemFk: true },
'sort-by': 'id ASC',
dense: true,
},
event: getInputEvents,
}, },
stickers: { {
component: VnInput, align: 'right',
props: { label: t('Size'),
type: 'number', name: 'size',
min: 0,
class: 'input-number',
dense: true,
},
event: getInputEvents,
}, },
printedStickers: { {
component: VnInput, label: t('Stickers'),
props: { name: 'stickers',
type: 'number', component: 'number',
min: 0, inputStyle: 'text-align: right',
class: 'input-number',
dense: true,
},
event: getInputEvents,
}, },
weight: { {
component: VnInput, label: t('Packaging'),
props: { name: 'packagingFk',
type: 'number', component: 'select',
min: 0, attrs: {
dense: true, url: 'packagings',
fields: ['id', 'volume'],
optionLabel: 'id',
}, },
event: getInputEvents, inputStyle: 'text-align: right',
}, },
packing: { {
component: VnInput, label: t('Weight'),
props: { name: 'weight',
type: 'number', component: 'number',
min: 0, create: true,
dense: true, inputStyle: 'text-align: right',
},
event: getInputEvents,
}, },
grouping: { {
component: VnInput, label: t('Packing'),
props: { name: 'packing',
type: 'number', component: 'number',
min: 0, inputStyle: 'text-align: right',
dense: true,
},
event: getInputEvents,
}, },
buyingValue: { {
component: VnInput, label: t('Grouping'),
props: { name: 'grouping',
type: 'number', component: 'number',
min: 0, inputStyle: 'text-align: right',
dense: true,
},
event: getInputEvents,
}, },
price2: { {
component: VnInput, label: t('Quantity'),
props: { name: 'quantity',
type: 'number', component: 'number',
min: 0, inputStyle: 'text-align: right',
dense: true,
},
event: getInputEvents,
}, },
price3: { {
component: VnInput, label: t('Amount'),
props: { name: 'amount',
type: 'number', component: 'number',
min: 0, inputStyle: 'text-align: right',
dense: true,
},
event: getInputEvents,
}, },
import: { {
component: 'span', label: t('price2'),
props: {}, name: 'price2',
event: () => ({}), component: 'number',
inputStyle: 'text-align: right',
}, },
})); {
label: t('price3'),
const entriesTableColumns = computed(() => { name: 'price3',
return [ component: 'number',
{ inputStyle: 'text-align: right',
label: t('entry.summary.item'), },
field: 'itemFk', {
name: 'item', label: 'Min.',
align: 'left', name: 'minPrice',
}, component: 'number',
{ inputStyle: 'text-align: right',
label: t('entry.summary.quantity'), },
field: 'quantity', {
name: 'quantity', label: t('packingOut'),
align: 'left', name: 'packingOut',
}, component: 'number',
{ inputStyle: 'text-align: right',
label: t('entry.summary.package'), },
field: 'packagingFk', {
name: 'packagingFk', label: 'Com.',
align: 'left', name: 'comment',
}, component: 'input',
{ inputStyle: 'text-align: right',
label: t('entry.summary.stickers'), },
field: 'stickers', {
name: 'stickers', label: t('subName'),
align: 'left', name: 'subName',
}, inputStyle: 'text-align: right',
{ },
label: t('entry.buys.printedStickers'), {
field: 'printedStickers', label: t('tags'),
name: 'printedStickers', name: 'tags',
align: 'left', component: 'input',
}, inputStyle: 'text-align: right',
{ },
label: t('entry.summary.weight'), {
field: 'weight', label: t('companyName'),
name: 'weight', name: 'company_name',
align: 'left', component: 'input',
}, inputStyle: 'text-align: right',
{ },
label: t('entry.summary.packing'), ];
field: 'packing', onMounted(() => (stateStore.rightDrawer = false));
name: 'packing',
align: 'left',
},
{
label: t('entry.summary.grouping'),
field: 'grouping',
name: 'grouping',
align: 'left',
},
{
label: t('entry.summary.buyingValue'),
field: 'buyingValue',
name: 'buyingValue',
align: 'left',
format: (value) => toCurrency(value),
},
{
label: t('entry.buys.groupingPrice'),
field: 'price2',
name: 'price2',
align: 'left',
},
{
label: t('entry.buys.packingPrice'),
field: 'price3',
name: 'price3',
align: 'left',
},
{
label: t('entry.summary.import'),
name: 'import',
align: 'left',
format: (_, row) => toCurrency(row.buyingValue * row.quantity),
},
];
});
const copyOriginalRowsData = (rows) => {
originalRowDataCopy.value = JSON.parse(JSON.stringify(rows));
};
const saveChange = async (field, { rowIndex, row }) => {
try {
if (originalRowDataCopy.value[rowIndex][field] == row[field]) return;
await axios.patch(`Buys/${row.id}`, row);
originalRowDataCopy.value[rowIndex][field] = row[field];
} catch (err) {
console.error('Error saving changes', err);
}
};
const openRemoveDialog = async () => {
quasar
.dialog({
component: VnConfirm,
componentProps: {
title: t('Confirm deletion'),
message: t(
`Are you sure you want to delete this buy${
rowsSelected.value.length > 1 ? 's' : ''
}?`
),
data: rowsSelected.value,
},
})
.onOk(async () => {
try {
await deleteBuys();
const notifyMessage = t(
`Buy${rowsSelected.value.length > 1 ? 's' : ''} deleted`
);
notify(notifyMessage, 'positive');
} catch (err) {
console.error('Error deleting buys');
}
});
};
const deleteBuys = async () => {
await axios.post('Buys/deleteBuys', { buys: rowsSelected.value });
entryBuysPaginateRef.value.fetch();
};
const importBuys = () => {
router.push({ name: 'EntryBuysImport' });
};
const toggleGroupingMode = async (buy, mode) => {
try {
const groupingMode = mode === 'grouping' ? mode : 'packing';
const newGroupingMode = buy.groupingMode === groupingMode ? null : groupingMode;
const params = {
groupingMode: newGroupingMode,
};
await axios.patch(`Buys/${buy.id}`, params);
buy.groupingMode = newGroupingMode;
} catch (err) {
console.error('Error toggling grouping mode');
}
};
const lockIconType = (groupingMode, mode) => {
if (mode === 'packing') {
return groupingMode === 'packing' ? 'lock' : 'lock_open';
} else {
return groupingMode === 'grouping' ? 'lock' : 'lock_open';
}
};
</script> </script>
<template> <template>
<VnSubToolbar> <VnSubToolbar />
<template #st-actions> <VnTable
<QBtnGroup push style="column-gap: 10px"> ref="tableRef"
<slot name="moreBeforeActions" />
<QBtn
:label="t('globals.remove')"
color="primary"
icon="delete"
flat
@click="openRemoveDialog()"
:disable="!rowsSelected?.length"
:title="t('globals.remove')"
/>
</QBtnGroup>
</template>
</VnSubToolbar>
<VnPaginate
ref="entryBuysPaginateRef"
data-key="EntryBuys" data-key="EntryBuys"
:url="`Entries/${route.params.id}/getBuys`" :url="`Entries/${route.params.id}/getBuys`"
@on-fetch="copyOriginalRowsData($event)" :is-editable="true"
:right-search="false"
:columns="columns"
auto-load auto-load
:disable-option="{ card: true }"
> >
<template #body="{ rows }"> <template #column-name="{ row }">
<QTable <span class="link">
:rows="rows" {{ row?.name }}
:columns="entriesTableColumns" <ItemDescriptorProxy :id="row?.itemFk" />
selection="multiple" </span>
row-key="id"
class="full-width q-mt-md"
:grid="$q.screen.lt.md"
v-model:selected="rowsSelected"
:no-data-label="t('globals.noResults')"
>
<template #body="props">
<QTr>
<QTd>
<QCheckbox v-model="props.selected" />
</QTd>
<QTd
v-for="col in props.cols"
:key="col.name"
style="max-width: 100px"
>
<component
:is="tableColumnComponents[col.name].component"
v-bind="tableColumnComponents[col.name].props"
v-model="props.row[col.field]"
v-on="
tableColumnComponents[col.name].event(
col.field,
props
)
"
>
<template
v-if="
col.name === 'grouping' || col.name === 'packing'
"
#append
>
<QBtn
:icon="
lockIconType(props.row.groupingMode, col.name)
"
@click="toggleGroupingMode(props.row, col.name)"
class="cursor-pointer"
size="sm"
flat
dense
unelevated
push
:style="{
'font-variation-settings': `'FILL' ${
lockIconType(
props.row.groupingMode,
col.name
) === 'lock'
? 1
: 0
}`,
}"
/>
</template>
<template
v-if="col.name === 'item' || col.name === 'import'"
>
{{ col.value }}
</template>
<ItemDescriptorProxy
v-if="col.name === 'item'"
:id="props.row.item.id"
/>
</component>
</QTd>
</QTr>
<QTr no-hover class="full-width infoRow" style="column-span: all">
<QTd />
<QTd cols>
<span>{{ props.row.item.itemType.code }}</span>
</QTd>
<QTd>
<span>{{ props.row.item.size }}</span>
</QTd>
<QTd>
<span>{{ toCurrency(props.row.item.minPrice) }}</span>
</QTd>
<QTd colspan="7">
<span>{{ props.row.item.concept }}</span>
<span v-if="props.row.item.subName" class="subName">
{{ props.row.item.subName }}
</span>
<FetchedTags :item="props.row.item" />
</QTd>
</QTr>
</template>
<template #item="props">
<div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition">
<QCard bordered flat>
<QCardSection>
<QCheckbox v-model="props.selected" dense />
</QCardSection>
<QSeparator />
<QList dense>
<QItem v-for="col in props.cols" :key="col.name">
<component
:is="tableColumnComponents[col.name].component"
v-bind="tableColumnComponents[col.name].props"
v-model="props.row[col.field]"
v-on="
tableColumnComponents[col.name].event(
col.field,
props
)
"
class="full-width"
>
<template
v-if="
col.name === 'item' ||
col.name === 'import'
"
>
{{ col.label + ': ' + col.value }}
</template>
</component>
</QItem>
</QList>
</QCard>
</div>
</template>
</QTable>
</template> </template>
</VnPaginate> <template #column-tags="{ row }">
<FetchedTags :item="row" :max-length="3" />
<QPageSticky :offset="[20, 20]"> </template>
<QBtn fab icon="upload" color="primary" @click="importBuys()" /> </VnTable>
<QTooltip class="text-no-wrap">
{{ t('Import buys') }}
</QTooltip>
</QPageSticky>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>