0
0
Fork 0

Compare commits

...

3 Commits

25 changed files with 824 additions and 712 deletions

View File

@ -6,11 +6,11 @@ import useNotify from 'src/composables/useNotify.js';
const { notify } = useNotify(); const { notify } = useNotify();
export default boot(({ app }) => { export default boot(({ app }) => {
app.mixin(qFormMixin);
app.mixin(mainShortcutMixin);
app.directive('shortcut', keyShortcut);
app.config.errorHandler = function (err) { app.config.errorHandler = function (err) {
console.error(err); console.error(err);
notify('globals.error', 'negative', 'error'); notify('globals.error', 'negative', 'error');
}; };
app.directive('shortcut', keyShortcut);
app.mixin(qFormMixin);
app.mixin(mainShortcutMixin);
}); });

View File

@ -0,0 +1,34 @@
<script setup>
import { computed } from 'vue';
const props = defineProps({
colors: {
type: Array,
required: true,
validator: (value) => value.length <= 3,
},
});
const sectionHeight = computed(() => `${100 / props.colors.length}%`);
</script>
<template>
<div class="color-div">
<div
v-for="(color, index) in colors"
:key="index"
:style="{
backgroundColor: color,
height: sectionHeight,
width: '100%',
flexShrink: 0,
}"
/>
</div>
</template>
<style scoped>
.color-div {
display: flex;
flex-direction: column;
height: 100vh;
width: 100%;
}
</style>

View File

@ -14,6 +14,7 @@ import VnComponent from 'components/common/VnComponent.vue';
import VnUserLink from 'components/ui/VnUserLink.vue'; import VnUserLink from 'components/ui/VnUserLink.vue';
const model = defineModel(undefined, { required: true }); const model = defineModel(undefined, { required: true });
const emit = defineEmits(['blur']);
const $props = defineProps({ const $props = defineProps({
column: { column: {
type: Object, type: Object,
@ -39,6 +40,10 @@ const $props = defineProps({
type: Object, type: Object,
default: null, default: null,
}, },
autofocus: {
type: Boolean,
default: false,
},
showLabel: { showLabel: {
type: Boolean, type: Boolean,
default: null, default: null,
@ -99,6 +104,7 @@ const defaultComponents = {
}, },
}, },
checkbox: { checkbox: {
ref: 'checkbox',
component: markRaw(QCheckbox), component: markRaw(QCheckbox),
attrs: ({ model }) => { attrs: ({ model }) => {
const defaultAttrs = { const defaultAttrs = {
@ -115,6 +121,10 @@ const defaultComponents = {
}, },
forceAttrs: { forceAttrs: {
label: $props.showLabel && $props.column.label, label: $props.showLabel && $props.column.label,
autofocus: true,
},
events: {
blur: () => emit('blur'),
}, },
}, },
select: { select: {
@ -160,7 +170,27 @@ const col = computed(() => {
return newColumn; return newColumn;
}); });
const components = computed(() => $props.components ?? defaultComponents); const components = computed(() => {
const sourceComponents = $props.components ?? defaultComponents;
return Object.keys(sourceComponents).reduce((acc, key) => {
const component = sourceComponents[key];
if (!component || typeof component !== 'object') {
acc[key] = component;
return acc;
}
acc[key] = {
...component,
attrs: {
...(component.attrs || {}),
autofocus: $props.autofocus,
},
};
return acc;
}, {});
});
</script> </script>
<template> <template>
<div class="row no-wrap"> <div class="row no-wrap">
@ -170,6 +200,7 @@ const components = computed(() => $props.components ?? defaultComponents);
:components="components" :components="components"
:value="{ row, model }" :value="{ row, model }"
v-model="model" v-model="model"
@blur="emit('blur')"
/> />
<VnComponent <VnComponent
v-if="col.component" v-if="col.component"
@ -177,6 +208,7 @@ const components = computed(() => $props.components ?? defaultComponents);
:components="components" :components="components"
:value="{ row, model }" :value="{ row, model }"
v-model="model" v-model="model"
@blur="emit('blur')"
/> />
<span :title="value" v-else>{{ value }}</span> <span :title="value" v-else>{{ value }}</span>
<VnComponent <VnComponent
@ -185,6 +217,7 @@ const components = computed(() => $props.components ?? defaultComponents);
:components="components" :components="components"
:value="{ row, model }" :value="{ row, model }"
v-model="model" v-model="model"
@blur="emit('blur')"
/> />
</div> </div>
</template> </template>

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

@ -51,11 +51,11 @@ defineExpose({ orderBy });
@mouseenter="hover = true" @mouseenter="hover = true"
@mouseleave="hover = false" @mouseleave="hover = false"
@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 title"
> >
<span :title="label">{{ label }}</span> <span :title="label">{{ label }}</span>
<sup v-if="name && model?.index">
<QChip <QChip
v-if="name"
: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"
@ -91,5 +91,20 @@ defineExpose({ orderBy });
/> />
</div> </div>
</QChip> </QChip>
</sup>
</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);
}
sup {
vertical-align: super; /* Valor predeterminado */
/* También puedes usar otros valores como "baseline", "top", "text-top", etc. */
}
</style>

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { ref, onBeforeMount, onMounted, computed, watch } from 'vue'; import { ref, onBeforeMount, onMounted, computed, watch, nextTick } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
@ -15,6 +15,7 @@ import VnTableChip from 'components/VnTable/VnChip.vue';
import VnVisibleColumn from 'src/components/VnTable/VnVisibleColumn.vue'; import VnVisibleColumn from 'src/components/VnTable/VnVisibleColumn.vue';
import VnLv from 'components/ui/VnLv.vue'; import VnLv from 'components/ui/VnLv.vue';
import VnTableOrder from 'src/components/VnTable/VnOrder.vue'; import VnTableOrder from 'src/components/VnTable/VnOrder.vue';
import { dashIfEmpty } from 'src/filters';
const $props = defineProps({ const $props = defineProps({
columns: { columns: {
@ -321,7 +322,149 @@ function handleOnDataSaved(_, res) {
if (_.onDataSaved) _.onDataSaved({ CrudModelRef: CrudModelRef.value }); if (_.onDataSaved) _.onDataSaved({ CrudModelRef: CrudModelRef.value });
else $props.create.onDataSaved(_); else $props.create.onDataSaved(_);
} }
const editingRow = ref(null);
const editingField = ref(null);
const handleClick = (event) => {
console.log('event: ', event);
const clickedElement = event.target.closest('td');
if (!clickedElement) return;
const rowIndex = clickedElement.getAttribute('data-row-index');
const colField = clickedElement.getAttribute('data-col-field');
if (rowIndex !== null && colField) {
startEditing(Number(rowIndex), colField);
}
};
const vnEditableCell = ref(null);
const startEditing = async (rowId, field) => {
console.log('entrando a startEditing');
const col = $props.columns.find((col) => col.name === field);
if (col?.isEditable === false) return;
editingRow.value = rowId;
editingField.value = field;
if (col.component === 'checkbox') {
await nextTick();
const inputElement = vnEditableCell.value?.$el?.querySelector('span > div');
inputElement.focus();
}
};
const stopEditing = (rowIndex, field) => {
if (editingRow.value !== rowIndex || editingField.value !== field) return;
editingRow.value = null;
editingField.value = null;
};
const findNextEditableColumnIndex = (columns, startIndex) => {
let index = startIndex;
console.log('columns: ', columns);
console.log('index: ', index);
console.log(
'columns[index]?.isVisible === false || columns[index]?.isEditable === false: ',
columns[index]?.isVisible === false || columns[index]?.isEditable === false
);
while (columns[index]?.isVisible === false && columns[index]?.isEditable === false) {
index++;
if (index >= columns.length) {
index = 0;
}
if (index === startIndex) {
return -1;
}
}
console.log('index: ', index);
return index;
};
const handleTab = async (rowIndex, colName) => {
console.log('colName: ', colName);
console.log('rowIndex: ', rowIndex);
const columns = $props.columns;
console.log('columns: ', columns);
if (!Array.isArray(columns) || columns.length === 0) return;
let currentColumnIndex = columns.findIndex((col) => col.name === colName);
if (currentColumnIndex === -1) return;
currentColumnIndex++;
if (currentColumnIndex >= columns.length) {
currentColumnIndex = 0;
rowIndex++;
}
currentColumnIndex = findNextEditableColumnIndex(columns, currentColumnIndex);
if (currentColumnIndex === -1) return;
await startEditing(rowIndex, columns[currentColumnIndex].name);
};
const handleShiftTab = async (rowIndex, colName) => {
console.log('handleShiftTab: ');
const columns = $props.columns;
const currentColumnIndex = columns.findIndex((col) => col.name === colName);
if (currentColumnIndex === -1) return;
let prevColumnIndex = currentColumnIndex - 1;
let prevRowIndex = rowIndex;
while (prevColumnIndex >= 0 && columns[prevColumnIndex]?.isEditable === false) {
prevColumnIndex--;
}
if (prevColumnIndex < 0) {
prevColumnIndex = columns.length - 1;
prevRowIndex -= 1;
while (prevRowIndex >= 0 && columns[prevColumnIndex]?.isEditable === false) {
prevColumnIndex--;
if (prevColumnIndex < 0) {
prevColumnIndex = columns.length - 1;
prevRowIndex--;
}
}
}
if (prevRowIndex < 0) {
stopEditing(rowIndex, colName);
return;
}
await startEditing(prevRowIndex, columns[prevColumnIndex]?.name);
console.log('finishHandleShiftTab');
};
const handleTabKey = async (event, rowIndex, colName) => {
console.log('colName: ', colName);
console.log('rowIndex: ', rowIndex);
console.log('event: ', event);
if (event.shiftKey) await handleShiftTab(rowIndex, colName);
else await handleTab(rowIndex, colName);
};
function getCheckboxIcon(value) {
switch (value) {
case true:
return 'check';
case false:
return 'close';
default:
return 'unknown_med';
}
}
function shouldDisplayReadonly(col, rowIndex) {
return (
col?.isEditable === false ||
editingRow.value !== rowIndex ||
editingField.value !== col?.name
);
}
</script> </script>
<template> <template>
<QDrawer <QDrawer
v-if="$props.rightSearch" v-if="$props.rightSearch"
@ -406,7 +549,8 @@ function handleOnDataSaved(_, res) {
<template #body="{ rows }"> <template #body="{ rows }">
<QTable <QTable
v-bind="table" v-bind="table"
class="vnTable" :class="['vnTable', table ? 'selection-cell' : '']"
wrap-cells
:columns="splittedColumns.columns" :columns="splittedColumns.columns"
:rows="rows" :rows="rows"
v-model:selected="selected" v-model:selected="selected"
@ -424,8 +568,15 @@ function handleOnDataSaved(_, res) {
" "
@row-click="(_, row) => rowClickFunction && rowClickFunction(row)" @row-click="(_, row) => rowClickFunction && rowClickFunction(row)"
@update:selected="emit('update:selected', $event)" @update:selected="emit('update:selected', $event)"
@click="handleClick"
> >
<template #top-left v-if="!$props.withoutHeader"> <template #top-left v-if="!$props.withoutHeader">
<QIcon
v-if="$props.isEditable"
name="edit"
color="primary"
size="sm"
/>
<slot name="top-left"> </slot> <slot name="top-left"> </slot>
</template> </template>
<template #top-right v-if="!$props.withoutHeader"> <template #top-right v-if="!$props.withoutHeader">
@ -451,13 +602,16 @@ 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="body-cell"
:style="col?.width ? `max-width: ${col?.width}` : ''"
>
<div <div
class="column self-start q-ml-xs ellipsis" class="no-padding"
: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]"
@ -468,7 +622,7 @@ function handleOnDataSaved(_, res) {
/> />
</div> </div>
<VnFilter <VnFilter
v-if="$props.columnSearch" v-if="$props.columnSearch && col.columnSearch !== false"
:column="col" :column="col"
:show-title="true" :show-title="true"
:data-key="$attrs['data-key']" :data-key="$attrs['data-key']"
@ -492,15 +646,28 @@ function handleOnDataSaved(_, res) {
</QTd> </QTd>
</template> </template>
<template #body-cell="{ col, row, rowIndex }"> <template #body-cell="{ col, row, rowIndex }">
<!-- Columns -->
<QTd <QTd
auto-width
class="no-margin q-px-xs"
:class="[getColAlign(col), col.columnClass]"
v-if="col.visible ?? true" v-if="col.visible ?? true"
@click.ctrl=" :style="{
($event) => 'max-width': col?.width ?? false,
rowCtrlClickFunction && rowCtrlClickFunction($event, row) position: 'relative',
}"
:class="[
getColAlign(col),
col.columnClass,
'body-cell no-margin no-padding',
]"
:data-row-index="rowIndex"
:data-col-field="col?.name"
>
<div
v-if="shouldDisplayReadonly(col, rowIndex)"
class="no-padding no-margin"
style="
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: middle;
" "
> >
<slot <slot
@ -509,14 +676,51 @@ function handleOnDataSaved(_, res) {
:row="row" :row="row"
:row-index="rowIndex" :row-index="rowIndex"
> >
<QIcon
v-if="col?.component === 'checkbox'"
:name="getCheckboxIcon(row[col?.name])"
style="color: var(--vn-text-color)"
size="var(--font-size)"
:class="
isEditable && (col?.isEditable ?? 'editable-text')
"
/>
<span
v-else
:class="
isEditable && (col?.isEditable ?? 'editable-text')
"
:style="col?.style ? col.style(row) : null"
>
{{
col?.format
? col.format(row, dashIfEmpty)
: dashIfEmpty(row[col?.name])
}}
</span>
</slot>
</div>
<div v-else-if="isEditable">
<VnTableColumn <VnTableColumn
ref="vnEditableCell"
:column="col" :column="col"
:row="row" :row="row"
:is-editable="col.isEditable ?? isEditable" :is-editable="col.isEditable ?? isEditable"
v-model="row[col.name]" v-model="row[col.name]"
component-prop="columnField" component-prop="columnField"
class="cell-input q-px-xs"
@blur="stopEditing(rowIndex, col?.name)"
@keyup.enter="stopEditing(rowIndex, col?.name)"
@keydown.tab.prevent="
handleTabKey($event, rowIndex, col?.name)
"
@keydown.shift.tab.prevent="
handleShiftTab(rowIndex, col?.name)
"
@keydown.escape="stopEditing(rowIndex, col?.name)"
:autofocus="true"
/> />
</slot> </div>
</QTd> </QTd>
</template> </template>
<template #body-cell-tableActions="{ col, row }"> <template #body-cell-tableActions="{ col, row }">
@ -757,6 +961,41 @@ es:
</i18n> </i18n>
<style lang="scss"> <style lang="scss">
.selection-cell {
table td:first-child {
padding: 0px;
}
}
.side-padding {
padding-left: 10px;
padding-right: 10px;
}
.editable-text:hover {
border-bottom: 1px dashed var(--q-primary);
@extend .side-padding;
}
.editable-text {
border-bottom: 1px dashed var(--vn-label-color);
@extend .side-padding;
}
.cell-input {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
padding-top: 0px !important;
}
.q-field--labeled .q-field__native,
.q-field--labeled .q-field__prefix,
.q-field--labeled .q-field__suffix {
padding-top: 20px;
}
.body-cell {
padding-left: 2px !important;
padding-right: 2px !important;
}
.bg-chip-secondary { .bg-chip-secondary {
background-color: var(--vn-page-color); background-color: var(--vn-page-color);
color: var(--vn-text-color); color: var(--vn-text-color);
@ -798,7 +1037,9 @@ es:
} }
} }
} }
.q-table tbody tr td {
position: relative;
}
.q-table { .q-table {
th { th {
padding: 0; padding: 0;
@ -821,21 +1062,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

@ -17,6 +17,8 @@ const $props = defineProps({
}, },
}); });
const emit = defineEmits(['blur']);
const componentArray = computed(() => { const componentArray = computed(() => {
if (typeof $props.prop === 'object') return [$props.prop]; if (typeof $props.prop === 'object') return [$props.prop];
return $props.prop; return $props.prop;
@ -54,6 +56,7 @@ function toValueAttrs(attrs) {
v-bind="mix(toComponent).attrs" v-bind="mix(toComponent).attrs"
v-on="mix(toComponent).event ?? {}" v-on="mix(toComponent).event ?? {}"
v-model="model" v-model="model"
@blur="emit('blur')"
/> />
</span> </span>
</template> </template>

View File

@ -1,4 +1,5 @@
<script setup> <script setup>
import { useAttrs } from 'vue';
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useValidator } from 'src/composables/useValidator'; import { useValidator } from 'src/composables/useValidator';
@ -8,6 +9,7 @@ const emit = defineEmits([
'update:options', 'update:options',
'keyup.enter', 'keyup.enter',
'remove', 'remove',
'blur',
]); ]);
const $props = defineProps({ const $props = defineProps({
@ -57,10 +59,6 @@ const focus = () => {
vnInputRef.value.focus(); vnInputRef.value.focus();
}; };
defineExpose({
focus,
});
import { useAttrs } from 'vue';
const $attrs = useAttrs(); const $attrs = useAttrs();
const mixinRules = [ const mixinRules = [
@ -76,6 +74,10 @@ const mixinRules = [
} }
}, },
]; ];
defineExpose({
focus,
});
</script> </script>
<template> <template>
@ -87,6 +89,7 @@ const mixinRules = [
:type="$attrs.type" :type="$attrs.type"
:class="{ required: $attrs.required }" :class="{ required: $attrs.required }"
@keyup.enter="emit('keyup.enter')" @keyup.enter="emit('keyup.enter')"
@blur="emit('blur')"
:clearable="false" :clearable="false"
:rules="mixinRules" :rules="mixinRules"
:lazy-rules="true" :lazy-rules="true"

View File

@ -27,6 +27,7 @@ const isPopupOpen = ref();
const hover = ref(); const hover = ref();
const mask = ref(); const mask = ref();
const $attrs = useAttrs(); const $attrs = useAttrs();
const emit = defineEmits(['blur']);
const mixinRules = [requiredFieldRule, ...($attrs.rules ?? [])]; const mixinRules = [requiredFieldRule, ...($attrs.rules ?? [])];
@ -102,6 +103,7 @@ const styleAttrs = computed(() => {
:rules="mixinRules" :rules="mixinRules"
:clearable="false" :clearable="false"
@click="isPopupOpen = true" @click="isPopupOpen = true"
@blur="emit('blur')"
hide-bottom-space hide-bottom-space
> >
<template #append> <template #append>

View File

@ -1,8 +1,9 @@
<script setup> <script setup>
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
const model = defineModel({ type: [Number, String] }); const model = defineModel({ type: [Number, String] });
const emit = defineEmits(['blur']);
</script> </script>
<template> <template>
<VnInput v-bind="$attrs" v-model.number="model" type="number" /> <VnInput v-bind="$attrs" v-model.number="model" type="number" @blur="emit('blur')" />
</template> </template>

View File

@ -24,6 +24,7 @@ const mixinRules = [requiredFieldRule, ...($attrs.rules ?? [])];
const dateFormat = 'HH:mm'; const dateFormat = 'HH:mm';
const isPopupOpen = ref(); const isPopupOpen = ref();
const hover = ref(); const hover = ref();
const emit = defineEmits(['blur']);
const styleAttrs = computed(() => { const styleAttrs = computed(() => {
return props.isOutlined return props.isOutlined
@ -80,6 +81,7 @@ function dateToTime(newDate) {
style="min-width: 100px" style="min-width: 100px"
:rules="mixinRules" :rules="mixinRules"
@click="isPopupOpen = false" @click="isPopupOpen = false"
@blur="emit('blur')"
type="time" type="time"
hide-bottom-space hide-bottom-space
> >

View File

@ -3,7 +3,7 @@ import { ref, toRefs, computed, watch, onMounted, useAttrs } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import FetchData from 'src/components/FetchData.vue'; import FetchData from 'src/components/FetchData.vue';
import { useValidator } from 'src/composables/useValidator'; import { useValidator } from 'src/composables/useValidator';
const emit = defineEmits(['update:modelValue', 'update:options', 'remove']); const emit = defineEmits(['update:modelValue', 'update:options', 'remove', 'blur']);
const $props = defineProps({ const $props = defineProps({
modelValue: { modelValue: {
@ -247,6 +247,7 @@ const getVal = (val) => ($props.useLike ? { like: `%${val}%` } : val);
:option-value="optionValue" :option-value="optionValue"
v-bind="$attrs" v-bind="$attrs"
@filter="filterHandler" @filter="filterHandler"
@blur="() => emit('blur')"
:emit-value="nullishToTrue($attrs['emit-value'])" :emit-value="nullishToTrue($attrs['emit-value'])"
:map-options="nullishToTrue($attrs['map-options'])" :map-options="nullishToTrue($attrs['map-options'])"
:use-input="nullishToTrue($attrs['use-input'])" :use-input="nullishToTrue($attrs['use-input'])"

View File

@ -14,7 +14,7 @@ const $props = defineProps({
}, },
}); });
const options = ref([]); const options = ref([]);
const emit = defineEmits(['blur']);
onBeforeMount(async () => { onBeforeMount(async () => {
const { url, optionValue, optionLabel } = useAttrs(); const { url, optionValue, optionLabel } = useAttrs();
const findBy = $props.find ?? url?.charAt(0)?.toLocaleLowerCase() + url?.slice(1, -1); const findBy = $props.find ?? url?.charAt(0)?.toLocaleLowerCase() + url?.slice(1, -1);
@ -35,5 +35,5 @@ onBeforeMount(async () => {
}); });
</script> </script>
<template> <template>
<VnSelect v-bind="$attrs" :options="$attrs.options ?? options" /> <VnSelect v-bind="$attrs" :options="$attrs.options ?? options" @blur="emit('blur')" />
</template> </template>

View File

@ -16,7 +16,13 @@ const $props = defineProps({
required: false, required: false,
default: 'value', default: 'value',
}, },
columns: {
type: Number,
required: false,
default: null, // Si es null, no se forzarán columnas
},
}); });
const tags = computed(() => { const tags = computed(() => {
return Object.keys($props.item) return Object.keys($props.item)
.filter((i) => i.startsWith(`${$props.tag}`)) .filter((i) => i.startsWith(`${$props.tag}`))
@ -28,10 +34,20 @@ const tags = computed(() => {
return acc; return acc;
}, {}); }, {});
}); });
const columnStyle = computed(() => {
if ($props.columns) {
return {
'grid-template-columns': `repeat(${$props.columns}, 1fr)`,
};
}
return {};
});
</script> </script>
<template> <template>
<div class="fetchedTags"> <div class="fetchedTags">
<div class="wrap"> <div class="wrap" :style="columnStyle">
<div <div
v-for="(val, key) in tags" v-for="(val, key) in tags"
:key="key" :key="key"
@ -39,7 +55,7 @@ const tags = computed(() => {
:title="`${key}: ${val}`" :title="`${key}: ${val}`"
:class="{ empty: !val }" :class="{ empty: !val }"
> >
{{ val }} <span>{{ val }} </span>
</div> </div>
</div> </div>
</div> </div>
@ -51,18 +67,17 @@ const tags = computed(() => {
.wrap { .wrap {
width: 100%; width: 100%;
flex-wrap: wrap; flex-wrap: wrap;
display: flex; display: grid; /* Cambiado a grid para poder controlar las columnas */
} }
.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;
flex: 1; border: 1px solid var(--vn-label-color);
border: 1px solid $color-spacer;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
min-width: 4rem; min-width: 4rem;

View File

@ -2,6 +2,10 @@
@import './icons.scss'; @import './icons.scss';
@import '@quasar/quasar-ui-qcalendar/src/QCalendarMonth.sass'; @import '@quasar/quasar-ui-qcalendar/src/QCalendarMonth.sass';
.tbody {
--vn-color-negative: $negative;
}
body.body--light { body.body--light {
--font-color: black; --font-color: black;
--vn-header-color: #cecece; --vn-header-color: #cecece;
@ -17,6 +21,8 @@ body.body--light {
.q-header .q-toolbar { .q-header .q-toolbar {
color: var(--font-color); color: var(--font-color);
} }
--vn-color-negative: $negative;
} }
body.body--dark { body.body--dark {
--vn-header-color: #5d5d5d; --vn-header-color: #5d5d5d;
@ -28,6 +34,8 @@ body.body--dark {
--vn-accent-color: #424242; --vn-accent-color: #424242;
background-color: var(--vn-page-color); background-color: var(--vn-page-color);
--vn-color-negative: $negative;
} }
a { a {
@ -136,11 +144,6 @@ select:-webkit-autofill {
cursor: pointer; cursor: pointer;
} }
.vn-table-separation-row {
height: 16px !important;
background-color: var(--vn-section-color) !important;
}
/* Estilo para el asterisco en campos requeridos */ /* Estilo para el asterisco en campos requeridos */
.q-field.required .q-field__label:after { .q-field.required .q-field__label:after {
content: ' *'; content: ' *';
@ -240,7 +243,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;
@ -256,8 +258,6 @@ input::-webkit-inner-spin-button {
font-size: 11pt; font-size: 11pt;
} }
td { td {
font-size: 11pt;
border-top: 1px solid var(--vn-page-color);
border-collapse: collapse; border-collapse: collapse;
} }
} }

View File

@ -45,3 +45,6 @@ $color-font-secondary: #777;
.bg-alert { .bg-alert {
background-color: $negative; background-color: $negative;
} }
.c-negative {
color: $negative;
}

View File

@ -1,82 +1,95 @@
<script setup> <template>
import { useI18n } from 'vue-i18n'; <q-page class="q-pa-md">
import { ref, computed } from 'vue'; <q-table title="Editable Table" :rows="rows" :columns="columns" row-key="id">
import VnTable from 'components/VnTable/VnTable.vue'; <template #body-cell="props">
import VnSearchbar from 'components/ui/VnSearchbar.vue'; <q-td :props="props">
import { useStateStore } from 'stores/useStateStore'; <div
v-if="
editingRow !== props.row.id || editingField !== props.col.name
"
@dblclick="startEditing(props.row.id, props.col.name)"
>
{{ props.row[props.col.name] }}
</div>
<div v-else>
<q-input
v-model="props.row[props.col.name]"
dense
autofocus
@blur="stopEditing"
@keyup.enter="stopEditing"
/>
</div>
</q-td>
</template>
</q-table>
</q-page>
</template>
const tableRef = ref(); <script>
const { t } = useI18n(); import { ref } from 'vue';
const stateStore = useStateStore();
const exprBuilder = (param, value) => { export default {
switch (param) { name: 'EditableTable',
case 'search': setup() {
return /^\d+$/.test(value) const editingRow = ref(null);
? { id: value } const editingField = ref(null);
: { alias: { like: `%${value}%` } };
} const rows = ref([
}; { id: 1, name: 'Apple', quantity: 10, price: 1.99 },
const columns = computed(() => [ { id: 2, name: 'Banana', quantity: 5, price: 0.99 },
{ id: 3, name: 'Orange', quantity: 8, price: 1.29 },
]);
const columns = ref([
{ {
name: 'name',
required: true,
label: 'Product Name',
align: 'left', align: 'left',
name: 'id', field: 'name',
label: t('Id'),
isId: true,
cardVisible: true,
}, },
{ {
name: 'quantity',
required: true,
label: 'Quantity',
align: 'left', align: 'left',
name: 'alias', field: 'quantity',
label: t('Alias'),
cardVisible: true,
create: true,
}, },
{ {
name: 'price',
required: true,
label: 'Price',
align: 'left', align: 'left',
name: 'description', field: 'price',
label: t('Description'), format: (val) => `$${val.toFixed(2)}`,
cardVisible: true,
create: true,
}, },
]); ]);
const startEditing = (rowId, field) => {
editingRow.value = rowId;
editingField.value = field;
};
const stopEditing = () => {
editingRow.value = null;
editingField.value = null;
};
return {
rows,
columns,
editingRow,
editingField,
startEditing,
stopEditing,
};
},
};
</script> </script>
<template> <style scoped>
<template v-if="stateStore.isHeaderMounted()"> .q-td {
<Teleport to="#searchbar"> cursor: pointer;
<VnSearchbar }
data-key="AccountAliasList" </style>
url="MailAliases"
:expr-builder="exprBuilder"
:label="t('mailAlias.search')"
:info="t('mailAlias.searchInfo')"
/>
</Teleport>
</template>
<VnTable
ref="tableRef"
data-key="AccountAliasList"
url="MailAliases"
:create="{
urlCreate: 'MailAliases',
title: 'Create MailAlias',
onDataSaved: ({ id }) => tableRef.redirect(id),
formInitialData: {},
}"
order="id DESC"
:columns="columns"
:disable-option="{ card: true }"
default-mode="table"
redirect="account/alias"
:is-editable="true"
:use-model="true"
/>
</template>
<i18n>
es:
Id: Id
Alias: Alias
Description: Descripción
</i18n>

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

@ -0,0 +1,68 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Checkbox Focus with Button</title>
<style>
body {
font-family: Arial, sans-serif;
}
.checkbox-container {
display: flex;
align-items: center;
margin-bottom: 20px;
}
input[type='checkbox'] {
width: 20px;
height: 20px;
}
label {
margin-left: 10px;
}
/* Estilos para el foco */
input[type='checkbox']:focus {
outline: 2px solid blue;
outline-offset: 2px;
}
</style>
</head>
<body>
<div class="checkbox-container">
<input type="checkbox" id="myCheckbox" />
<label for="myCheckbox">Acepto los términos y condiciones</label>
</div>
<!-- Botón para enfocar el checkbox -->
<button id="focusButton">Dar foco al checkbox</button>
<script>
const checkbox = document.getElementById('myCheckbox');
const focusButton = document.getElementById('focusButton');
// Manejador de eventos para cuando el checkbox recibe el foco
checkbox.addEventListener('focus', () => {
console.log('El checkbox tiene el foco');
});
// Manejador de eventos para cuando el checkbox pierde el foco
checkbox.addEventListener('blur', () => {
console.log('El checkbox perdió el foco');
});
// Manejador de eventos para cuando se cambia el estado del checkbox
checkbox.addEventListener('change', (event) => {
if (event.target.checked) {
console.log('El checkbox está marcado');
} else {
console.log('El checkbox no está marcado');
}
});
// Dar foco al checkbox cuando se presiona el botón
focusButton.addEventListener('click', () => {
checkbox.focus();
});
</script>
</body>
</html>

View File

@ -157,6 +157,7 @@ const onFilterTravelSelected = (formData, id) => {
</VnRow> </VnRow>
<VnRow> <VnRow>
<QCheckbox <QCheckbox
v-focus
v-model="data.isOrdered" v-model="data.isOrdered"
:label="t('entry.basicData.ordered')" :label="t('entry.basicData.ordered')"
/> />

View File

@ -1,491 +1,243 @@
<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, ref } 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 VnColor from 'src/components/VnColor.vue';
import { useQuasar } from 'quasar';
import { toCurrency } from 'src/filters';
import axios from 'axios';
import useNotify from 'src/composables/useNotify.js';
const quasar = useQuasar();
const route = useRoute();
const router = useRouter();
const { t } = useI18n(); const { t } = useI18n();
const { notify } = useNotify(); const stateStore = useStateStore();
const route = useRoute();
const rowsSelected = ref([]); const selectedRows = ref([]);
const entryBuysPaginateRef = ref(null); const columns = [
const originalRowDataCopy = ref(null);
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,
props: {
type: 'number',
min: 0,
class: 'input-number',
dense: true,
},
event: getInputEvents,
},
packagingFk: {
component: VnSelect,
props: {
'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,
props: {
type: 'number',
min: 0,
class: 'input-number',
dense: true,
},
event: getInputEvents,
},
printedStickers: {
component: VnInput,
props: {
type: 'number',
min: 0,
class: 'input-number',
dense: true,
},
event: getInputEvents,
},
weight: {
component: VnInput,
props: {
type: 'number',
min: 0,
dense: true,
},
event: getInputEvents,
},
packing: {
component: VnInput,
props: {
type: 'number',
min: 0,
dense: true,
},
event: getInputEvents,
},
grouping: {
component: VnInput,
props: {
type: 'number',
min: 0,
dense: true,
},
event: getInputEvents,
},
buyingValue: {
component: VnInput,
props: {
type: 'number',
min: 0,
dense: true,
},
event: getInputEvents,
},
price2: {
component: VnInput,
props: {
type: 'number',
min: 0,
dense: true,
},
event: getInputEvents,
},
price3: {
component: VnInput,
props: {
type: 'number',
min: 0,
dense: true,
},
event: getInputEvents,
},
import: {
component: 'span',
props: {},
event: () => ({}),
},
}));
const entriesTableColumns = computed(() => {
return [
{ {
label: t('entry.summary.item'), name: 'buyFk',
field: 'itemFk', isId: true,
name: 'item', visible: false,
align: 'left', isEditable: false,
}, },
{ {
label: t('entry.summary.quantity'), align: 'center',
field: 'quantity', label: 'Nv',
name: 'quantity', name: 'isIgnored',
align: 'left', component: 'checkbox',
width: '35px',
}, },
{ {
label: t('entry.summary.package'), align: 'center',
field: 'packagingFk', label: 'Id',
name: 'packagingFk', name: 'itemFk',
align: 'left', component: 'input',
create: true,
width: '45px',
}, },
{ {
label: t('entry.summary.stickers'), label: '',
field: 'stickers', name: 'hex',
columnSearch: false,
isEditable: false,
width: '5px',
},
{
align: 'center',
label: t('Article'),
name: 'name',
width: '100px',
isEditable: false,
},
{
align: 'center',
label: t('Size'),
name: 'size',
width: '35px',
isEditable: false,
style: () => {
return { color: 'var(--vn-label-color)' };
},
},
{
align: 'center',
label: t('Sti.'),
name: 'stickers', name: 'stickers',
align: 'left', component: 'number',
width: '35px',
}, },
{ {
label: t('entry.buys.printedStickers'), align: 'center',
field: 'printedStickers', label: t('Bucket'),
name: 'printedStickers', name: 'packagingFk',
align: 'left', component: 'select',
attrs: {
url: 'packagings',
fields: ['id', 'volume'],
optionLabel: 'id',
},
width: '60px',
}, },
{ {
label: t('entry.summary.weight'), align: 'center',
field: 'weight', label: 'Kg',
name: 'weight', name: 'weight',
align: 'left', component: 'number',
create: true,
width: '35px',
}, },
{ {
label: t('entry.summary.packing'), label: 'Pack',
field: 'packing',
name: 'packing', name: 'packing',
align: 'left', component: 'number',
width: '35px',
style: (row) => {
if (row.groupingMode === 'grouping') {
return { color: 'var(--vn-label-color)' };
}
},
}, },
{ {
label: t('entry.summary.grouping'), label: 'Group',
field: 'grouping',
name: 'grouping', name: 'grouping',
align: 'left', component: 'number',
width: '35px',
style: (row) => {
if (row.groupingMode === 'packing') {
return { color: 'var(--vn-label-color)' };
}
},
}, },
{ {
label: t('entry.summary.buyingValue'), label: t('Quantity'),
field: 'buyingValue', name: 'quantity',
name: 'buyingValue', component: 'number',
align: 'left', width: '50px',
format: (value) => toCurrency(value), style: (row) => {
if (row?.quantity !== row?.stickers * row?.packing)
return { color: 'var(--q-negative)' };
},
}, },
{ {
label: t('entry.buys.groupingPrice'), label: t('Amount'),
field: 'price2', name: 'amount',
component: 'number',
width: '50px',
},
{
label: t('Package'),
name: 'price2', name: 'price2',
align: 'left', component: 'number',
width: '35px',
}, },
{ {
label: t('entry.buys.packingPrice'), label: t('Box'),
field: 'price3',
name: 'price3', name: 'price3',
align: 'left', component: 'number',
width: '35px',
}, },
{ {
label: t('entry.summary.import'), align: 'center',
name: 'import', label: 'Min.',
align: 'left', name: 'minPrice',
format: (_, row) => toCurrency(row.buyingValue * row.quantity), component: 'number',
width: '35px',
style: (row) => {
if (row?.hasMinPrice) {
return { backgroundColor: 'var(--q-positive)', color: 'black' };
}
},
},
{
label: t('P.Sen'),
name: 'packingOut',
component: 'number',
width: '40px',
},
{
align: 'center',
label: t('Com.'),
name: 'comment',
component: 'input',
width: '55px',
},
{
label: 'Prod.',
name: 'subName',
component: 'input',
width: '45px',
},
{
align: 'center',
label: 'Tags',
name: 'tags',
width: '120px',
columnSearch: false,
isEditable: false,
},
{
align: 'center',
label: 'Comp.',
name: 'company_name',
component: 'input',
width: '35px',
}, },
]; ];
onMounted(() => {
stateStore.rightDrawer = false;
}); });
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)" :disable-option="{ card: true }"
:right-search="false"
:row-click="false"
:columns="columns"
class="buyList"
is-editable
auto-load auto-load
> >
<template #body="{ rows }"> <template #column-hex>
<QTable <VnColor :colors="['#ff0000', '#ffff00', '#ff0000']" style="height: 100%" />
:rows="rows"
:columns="entriesTableColumns"
selection="multiple"
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>
<template <template #column-name="{ row }">
v-if="col.name === 'item' || col.name === 'import'" <span class="link">
> {{ row?.name }}
{{ col.value }} <ItemDescriptorProxy :id="row?.itemFk" />
</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> </span>
<FetchedTags :item="props.row.item" />
</QTd>
</QTr>
</template> </template>
<template #item="props"> <template #column-tags="{ row }">
<div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition"> <FetchedTags :item="row" :columns="3" />
<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> </template>
</component> <template #column-stickers="{ row }">
</QItem> <span style="color: var(--vn-label-color)">{{ row.printedStickers }}</span>
</QList> <span>/{{ row.stickers }}</span>
</QCard>
</div>
</template> </template>
</QTable> </VnTable>
</template> </template>
</VnPaginate>
<QPageSticky :offset="[20, 20]">
<QBtn fab icon="upload" color="primary" @click="importBuys()" />
<QTooltip class="text-no-wrap">
{{ t('Import buys') }}
</QTooltip>
</QPageSticky>
</template>
<style lang="scss" scoped> <style lang="scss" scoped>
.q-table--horizontal-separator tbody tr:nth-child(odd) > td { .q-checkbox__inner--dark {
border-bottom-width: 0px; &__inner {
border-top-width: 2px; border-radius: 0% !important;
border-color: var(--vn-text-color); background-color: rosybrown;
} }
.infoRow > td {
color: var(--vn-label-color);
} }
</style> </style>
<i18n> <i18n>
es: es:
Import buys: Importar compras Article: Artículo
Buy deleted: Compra eliminada Size: Med.
Buys deleted: Compras eliminadas Sti.: Eti.
Confirm deletion: Confirmar eliminación Bucket: Cubo
Are you sure you want to delete this buy?: Seguro que quieres eliminar esta compra? Quantity: Cantidad
Are you sure you want to delete this buys?: Seguro que quieres eliminar estas compras? Amount: Importe
Package: Paquete
Box: Caja
P.Sen: P.Env
Com.: Ref.
</i18n> </i18n>

View File

@ -271,68 +271,6 @@ const fetchEntryBuys = async () => {
:disable="true" :disable="true"
/> />
</QCard> </QCard>
<QCard class="vn-two" style="min-width: 100%">
<a class="header header-link">
{{ t('entry.summary.buys') }}
<QIcon name="open_in_new" />
</a>
<QTable
:rows="entryBuys"
:columns="entriesTableColumns"
row-key="index"
class="full-width q-mt-md"
:no-data-label="t('globals.noResults')"
>
<template #body="{ cols, row, rowIndex }">
<QTr no-hover>
<QTd v-for="col in cols" :key="col?.name">
<component
:is="tableColumnComponents[col?.name].component()"
v-bind="tableColumnComponents[col?.name].props()"
@click="tableColumnComponents[col?.name].event()"
class="col-content"
>
<template
v-if="
col?.name !== 'observation' &&
col?.name !== 'isConfirmed'
"
>{{ col.value }}</template
>
<QTooltip v-if="col.toolTip">{{
col.toolTip
}}</QTooltip>
</component>
</QTd>
</QTr>
<QTr no-hover>
<QTd>
<span>{{ row.item.itemType.code }}</span>
</QTd>
<QTd>
<span>{{ row.item.id }}</span>
</QTd>
<QTd>
<span>{{ row.item.size }}</span>
</QTd>
<QTd>
<span>{{ toCurrency(row.item.minPrice) }}</span>
</QTd>
<QTd colspan="6">
<span>{{ row.item.concept }}</span>
<span v-if="row.item.subName" class="subName">
{{ row.item.subName }}
</span>
<FetchedTags :item="row.item" />
</QTd>
</QTr>
<!-- Esta última row es utilizada para agregar un espaciado y así marcar una diferencia visual entre los diferentes buys -->
<QTr v-if="rowIndex !== entryBuys.length - 1">
<QTd colspan="10" class="vn-table-separation-row" />
</QTr>
</template>
</QTable>
</QCard>
</template> </template>
</CardSummary> </CardSummary>
</template> </template>

View File

@ -89,7 +89,7 @@ const columns = computed(() => [
format: (row, dashIfEmpty) => dashIfEmpty(row.supplierName), format: (row, dashIfEmpty) => dashIfEmpty(row.supplierName),
}, },
{ {
align: 'left', align: 'center',
label: t('entry.list.tableVisibleColumns.isBooked'), label: t('entry.list.tableVisibleColumns.isBooked'),
name: 'isBooked', name: 'isBooked',
cardVisible: true, cardVisible: true,
@ -97,7 +97,7 @@ const columns = computed(() => [
component: 'checkbox', component: 'checkbox',
}, },
{ {
align: 'left', align: 'center',
label: t('entry.list.tableVisibleColumns.isConfirmed'), label: t('entry.list.tableVisibleColumns.isConfirmed'),
name: 'isConfirmed', name: 'isConfirmed',
cardVisible: true, cardVisible: true,
@ -105,7 +105,7 @@ const columns = computed(() => [
component: 'checkbox', component: 'checkbox',
}, },
{ {
align: 'left', align: 'center',
label: t('entry.list.tableVisibleColumns.isOrdered'), label: t('entry.list.tableVisibleColumns.isOrdered'),
name: 'isOrdered', name: 'isOrdered',
cardVisible: true, cardVisible: true,
@ -154,7 +154,7 @@ const columns = computed(() => [
cardVisible: true, cardVisible: true,
}, },
{ {
align: 'left', align: 'center',
label: t('entry.list.tableVisibleColumns.isExcludedFromAvailable'), label: t('entry.list.tableVisibleColumns.isExcludedFromAvailable'),
name: 'isExcludedFromAvailable', name: 'isExcludedFromAvailable',
chip: { chip: {
@ -165,9 +165,10 @@ const columns = computed(() => [
columnFilter: { columnFilter: {
inWhere: true, inWhere: true,
}, },
component: 'checkbox',
}, },
{ {
align: 'left', align: 'center',
label: t('entry.list.tableVisibleColumns.isRaid'), label: t('entry.list.tableVisibleColumns.isRaid'),
name: 'isRaid', name: 'isRaid',
chip: { chip: {
@ -178,6 +179,7 @@ const columns = computed(() => [
columnFilter: { columnFilter: {
inWhere: true, inWhere: true,
}, },
component: 'checkbox',
}, },
{ {
align: 'right', align: 'right',

View File

@ -21,9 +21,8 @@ const $props = defineProps({
}, },
}); });
</script> </script>
<template> <template>
<QPopupProxy> <QPopupProxy style="max-width: 10px">
<ItemDescriptor <ItemDescriptor
v-if="$props.id" v-if="$props.id"
:id="$props.id" :id="$props.id"

View File

@ -94,6 +94,7 @@ const columns = computed(() => [
align: 'left', align: 'left',
name: 'hasDiploma', name: 'hasDiploma',
label: t('worker.formation.tableVisibleColumns.hasDiploma'), label: t('worker.formation.tableVisibleColumns.hasDiploma'),
component: 'checkbox',
create: true, create: true,
}, },
]); ]);