forked from verdnatura/salix-front
Compare commits
3 Commits
dev
...
6897-entry
Author | SHA1 | Date |
---|---|---|
|
54ace8c682 | |
|
6dd0b32389 | |
|
70decb68ea |
|
@ -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);
|
||||||
});
|
});
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
||||||
|
|
|
@ -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,
|
||||||
},
|
},
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
>
|
>
|
||||||
|
|
|
@ -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'])"
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,3 +45,6 @@ $color-font-secondary: #777;
|
||||||
.bg-alert {
|
.bg-alert {
|
||||||
background-color: $negative;
|
background-color: $negative;
|
||||||
}
|
}
|
||||||
|
.c-negative {
|
||||||
|
color: $negative;
|
||||||
|
}
|
||||||
|
|
|
@ -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>
|
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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>
|
|
@ -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')"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
Loading…
Reference in New Issue