#6917 - inputDate_inpuTime #498

Merged
alexm merged 16 commits from 6917-inputDate_inpuTime into dev 2024-07-03 06:52:47 +00:00
10 changed files with 198 additions and 228 deletions

View File

@ -90,7 +90,7 @@ const $props = defineProps({
}); });
const emit = defineEmits(['onFetch', 'onDataSaved']); const emit = defineEmits(['onFetch', 'onDataSaved']);
const modelValue = computed( const modelValue = computed(
() => $props.model ?? `formModel_${route?.meta?.title ?? route.name}` () => $props.model ?? `formModel_${route?.meta?.title ?? route.name}`,
).value; ).value;
const componentIsRendered = ref(false); const componentIsRendered = ref(false);
const arrayData = useArrayData(modelValue); const arrayData = useArrayData(modelValue);
@ -137,7 +137,7 @@ onMounted(async () => {
JSON.stringify(newVal) !== JSON.stringify(originalData.value); JSON.stringify(newVal) !== JSON.stringify(originalData.value);
isResetting.value = false; isResetting.value = false;
}, },
{ deep: true } { deep: true },
); );
} }
}); });
@ -145,7 +145,7 @@ onMounted(async () => {
if (!$props.url) if (!$props.url)
watch( watch(
() => arrayData.store.data, () => arrayData.store.data,
(val) => updateAndEmit('onFetch', val) (val) => updateAndEmit('onFetch', val),
); );
watch(formUrl, async () => { watch(formUrl, async () => {
@ -206,11 +206,11 @@ async function save() {
updateAndEmit('onDataSaved', formData.value, response?.data); updateAndEmit('onDataSaved', formData.value, response?.data);
if ($props.reload) await arrayData.fetch({}); if ($props.reload) await arrayData.fetch({});
hasChanges.value = false;
Review

Sino al fallar te dice que no tienes cambios, cuando realmente si hay. (Te deshabilitaba los botones)

Sino al fallar te dice que no tienes cambios, cuando realmente si hay. (Te deshabilitaba los botones)
} catch (err) { } catch (err) {
console.error(err); console.error(err);
notify('errors.writeRequest', 'negative'); notify('errors.writeRequest', 'negative');
} finally { } finally {
hasChanges.value = false;
isLoading.value = false; isLoading.value = false;
} }
} }
@ -239,7 +239,7 @@ function filter(value, update, filterOptions) {
(ref) => { (ref) => {
ref.setOptionIndex(-1); ref.setOptionIndex(-1);
ref.moveOptionSelection(1, true); ref.moveOptionSelection(1, true);
} },
); );
} }

View File

@ -7,6 +7,7 @@ import { useArrayData } from 'composables/useArrayData';
import VnSelect from 'components/common/VnSelect.vue'; import VnSelect from 'components/common/VnSelect.vue';
import VnInput from 'components/common/VnInput.vue'; import VnInput from 'components/common/VnInput.vue';
import VnInputDate from 'components/common/VnInputDate.vue'; import VnInputDate from 'components/common/VnInputDate.vue';
import VnInputTime from 'components/common/VnInputTime.vue';
import VnTableColumn from 'components/VnTable/VnColumn.vue'; import VnTableColumn from 'components/VnTable/VnColumn.vue';
const $props = defineProps({ const $props = defineProps({
@ -75,6 +76,17 @@ const components = {
}, },
forceAttrs, forceAttrs,
}, },
time: {
component: markRaw(VnInputTime),
event: updateEvent,
attrs: {
...defaultAttrs,
disable: !$props.isEditable,
},
forceAttrs: {
label: $props.showLabel && $props.column.label,
},
},
checkbox: { checkbox: {
component: markRaw(QCheckbox), component: markRaw(QCheckbox),
event: updateEvent, event: updateEvent,

View File

@ -1,84 +1,31 @@
<script setup> <script setup>
Outdated
Review

Lo he rehecho desde 0, no me fijaria en lo que había

Lo he rehecho desde 0, no me fijaria en lo que había
import { computed, ref } from 'vue'; import { onMounted, watch, computed, ref } from 'vue';
import { date } from 'quasar';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import isValidDate from 'filters/isValidDate';
const props = defineProps({ const model = defineModel({ type: String });
modelValue: { const $props = defineProps({
type: String,
default: null,
},
readonly: {
type: Boolean,
default: false,
},
isOutlined: { isOutlined: {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
emitDateFormat: {
type: Boolean,
default: false,
},
}); });
const hover = ref(false);
const emit = defineEmits(['update:modelValue']);
const { t } = useI18n(); const { t } = useI18n();
const requiredFieldRule = (val) => !!val || t('globals.fieldRequired'); const requiredFieldRule = (val) => !!val || t('globals.fieldRequired');
const joinDateAndTime = (date, time) => { const dateFormat = 'DD/MM/YYYY';
if (!date) { const isPopupOpen = ref();
return null; const hover = ref();
} const mask = ref();
if (!time) {
return new Date(date).toISOString();
}
const [year, month, day] = date.split('/');
return new Date(`${year}-${month}-${day}T${time}`).toISOString();
};
const time = computed(() => (props.modelValue ? props.modelValue.split('T')?.[1] : null)); onMounted(() => {
const value = computed({ // fix quasar bug
get() { mask.value = '##/##/####';
return props.modelValue;
},
set(value) {
emit(
'update:modelValue',
props.emitDateFormat ? new Date(value) : joinDateAndTime(value, time.value)
);
},
}); });
const isPopupOpen = ref(false);
const onDateUpdate = (date) => {
value.value = date;
isPopupOpen.value = false;
};
const padDate = (value) => value.toString().padStart(2, '0');
const formatDate = (dateString) => {
const date = new Date(dateString || '');
return `${date.getFullYear()}/${padDate(date.getMonth() + 1)}/${padDate(
date.getDate()
)}`;
};
const displayDate = (dateString) => {
if (!dateString || !isValidDate(dateString)) {
return '';
}
return new Date(dateString).toLocaleDateString([], {
year: 'numeric',
month: '2-digit',
day: '2-digit',
});
};
const styleAttrs = computed(() => { const styleAttrs = computed(() => {
return props.isOutlined return $props.isOutlined
? { ? {
dense: true, dense: true,
outlined: true, outlined: true,
@ -86,52 +33,101 @@ const styleAttrs = computed(() => {
} }
: {}; : {};
}); });
const formattedDate = computed({
get() {
if (!model.value) return model.value;
return date.formatDate(new Date(model.value), dateFormat);
},
set(value) {
if (value == model.value) return;
let newDate;
if (value) {
// parse input
if (value.includes('/') && value.length >= 10) {
if (value.at(2) == '/') value = value.split('/').reverse().join('/');
value = date.formatDate(
new Date(value).toISOString(),
'YYYY-MM-DDTHH:mm:ss.SSSZ'
);
}
let ymd = value.split('-').map((e) => parseInt(e));
newDate = new Date(ymd[0], ymd[1] - 1, ymd[2]);
if (model.value) {
const orgDate =
model.value instanceof Date ? model.value : new Date(model.value);
newDate.setHours(
orgDate.getHours(),
orgDate.getMinutes(),
orgDate.getSeconds(),
orgDate.getMilliseconds()
);
}
}
if (!isNaN(newDate)) model.value = newDate.toISOString();
},
});
const popupDate = computed(() =>
model.value ? date.formatDate(new Date(model.value), 'YYYY/MM/DD') : model.value
);
watch(
() => model.value,
(val) => (formattedDate.value = val),
{ immediate: true }
);
</script> </script>
<template> <template>
<div @mouseover="hover = true" @mouseleave="hover = false"> <div @mouseover="hover = true" @mouseleave="hover = false">
<QInput <QInput
v-model="formattedDate"
class="vn-input-date" class="vn-input-date"
readonly :mask="mask"
:model-value="displayDate(value)" placeholder="dd/mm/aaaa"
v-bind="{ ...$attrs, ...styleAttrs }" v-bind="{ ...$attrs, ...styleAttrs }"
:class="{ required: $attrs.required }" :class="{ required: $attrs.required }"
:rules="$attrs.required ? [requiredFieldRule] : null" :rules="$attrs.required ? [requiredFieldRule] : null"
@click="isPopupOpen = true" :clearable="false"
> >
<template #append> <template #append>
<QIcon <QIcon
name="close" name="close"
alexm marked this conversation as resolved Outdated

PAra que en TicketCreate.vue quiero que el campo sea clearable tengo que poner la propiedad a mano

PAra que en TicketCreate.vue quiero que el campo sea clearable tengo que poner la propiedad a mano
size="xs" size="xs"
v-if="hover && value && !readonly" v-if="
@click="onDateUpdate(null)" ($attrs.clearable == undefined || $attrs.clearable) &&
></QIcon> hover &&
<QIcon name="event" class="cursor-pointer"> model &&
<QPopupProxy !$attrs.disable
v-model="isPopupOpen" "
cover @click="
transition-show="scale" model = null;
transition-hide="scale" isPopupOpen = false;
:no-parent-event="props.readonly" "
> />
<QDate <QIcon name="event" class="cursor-pointer" />
:today-btn="true"
:model-value="formatDate(value)"
@update:model-value="onDateUpdate"
/>
</QPopupProxy>
</QIcon>
</template> </template>
<QMenu
transition-show="scale"
transition-hide="scale"
v-model="isPopupOpen"
anchor="bottom left"
self="top start"
:no-focus="true"
>
<QDate
v-model="popupDate"
:today-btn="true"
@update:model-value="
(date) => {
formattedDate = date;
isPopupOpen = false;
}
"
/>
</QMenu>
</QInput> </QInput>
</div> </div>
</template> </template>
<style lang="scss">
.vn-input-date.q-field--standard.q-field--readonly .q-field__control:before {
border-bottom-style: solid;
}
.vn-input-date.q-field--outlined.q-field--readonly .q-field__control:before {
border-style: solid;
}
</style>

View File

@ -1,14 +1,11 @@
<script setup> <script setup>
Outdated
Review

Lo he rehecho desde 0, no me fijaria en lo que habia

Lo he rehecho desde 0, no me fijaria en lo que habia
import { computed, ref } from 'vue'; import { watch, computed, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import isValidDate from 'filters/isValidDate'; import { date } from 'quasar';
const model = defineModel({ type: String });
const props = defineProps({ const props = defineProps({
modelValue: { timeOnly: {
type: String,
default: null,
},
readonly: {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
@ -17,43 +14,12 @@ const props = defineProps({
default: false, default: false,
}, },
}); });
const emit = defineEmits(['update:modelValue']);
const { t } = useI18n(); const { t } = useI18n();
const requiredFieldRule = (val) => !!val || t('globals.fieldRequired'); const requiredFieldRule = (val) => !!val || t('globals.fieldRequired');
const value = computed({ const dateFormat = 'HH:mm';
get() { const isPopupOpen = ref();
return props.modelValue; const hover = ref();
},
set(value) {
const [hours, minutes] = value.split(':');
const date = new Date(props.modelValue);
date.setHours(Number.parseInt(hours) || 0, Number.parseInt(minutes) || 0, 0, 0);
emit('update:modelValue', value ? date.toISOString() : null);
},
});
const isPopupOpen = ref(false);
const onDateUpdate = (date) => {
internalValue.value = date;
};
const save = () => {
value.value = internalValue.value;
};
const formatTime = (dateString) => {
if (!dateString || !isValidDate(dateString)) {
return '';
}
const date = new Date(dateString || '');
return date.toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit',
});
};
const internalValue = ref(formatTime(value));
const styleAttrs = computed(() => { const styleAttrs = computed(() => {
return props.isOutlined return props.isOutlined
@ -64,65 +30,84 @@ const styleAttrs = computed(() => {
} }
: {}; : {};
}); });
const formattedTime = computed({
get() {
if (!model.value || model.value?.length <= 5) return model.value;
return dateToTime(model.value);
},
set(value) {
if (value == model.value) return;
let time = value;
if (time) {
if (time?.length > 5) time = dateToTime(time);
if (!props.timeOnly) {
const hours = time.split(':');
const date = new Date();
date.setHours(hours[0], hours[1], 0);
time = date.toISOString();
}
}
model.value = time;
},
});
function dateToTime(newDate) {
return date.formatDate(new Date(newDate), dateFormat);
}
watch(
() => model.value,
(val) => (formattedTime.value = val),
{ immediate: true }
);
</script> </script>
<template> <template>
<QInput <div @mouseover="hover = true" @mouseleave="hover = false">
class="vn-input-time" <QInput
readonly class="vn-input-time"
:model-value="formatTime(value)" mask="##:##"
v-bind="{ ...$attrs, ...styleAttrs }" placeholder="--:--"
:class="{ required: $attrs.required }" v-model="formattedTime"
:rules="$attrs.required ? [requiredFieldRule] : null" v-bind="{ ...$attrs, ...styleAttrs }"
@click="isPopupOpen = true" :class="{ required: $attrs.required }"
> style="min-width: 100px"
<template #append> :rules="$attrs.required ? [requiredFieldRule] : null"
<QIcon name="Schedule" class="cursor-pointer"> >
<QPopupProxy <template #append>
v-model="isPopupOpen" <QIcon
cover name="close"
transition-show="scale" size="xs"
transition-hide="scale" v-if="
:no-parent-event="props.readonly" ($attrs.clearable == undefined || $attrs.clearable) &&
> hover &&
<QTime model &&
:format24h="false" !$attrs.disable
:model-value="formatTime(value)" "
@update:model-value="onDateUpdate" @click="
> model = null;
<div class="row items-center justify-end q-gutter-sm"> isPopupOpen = false;
<QBtn "
:label="t('Cancel')" />
color="primary" <QIcon name="Schedule" class="cursor-pointer" />
flat </template>
v-close-popup <QMenu
/> transition-show="scale"
<QBtn transition-hide="scale"
label="Ok" v-model="isPopupOpen"
color="primary" anchor="bottom left"
flat self="top start"
@click="save" :no-focus="true"
v-close-popup >
/> <QTime
</div> :format24h="false"
</QTime> v-model="formattedTime"
</QPopupProxy> mask="HH:mm"
</QIcon> landscape
</template> now-btn
</QInput> />
</QMenu>
</QInput>
</div>
</template> </template>
<style lang="scss">
.vn-input-time.q-field--standard.q-field--readonly .q-field__control:before {
border-bottom-style: solid;
}
.vn-input-time.q-field--outlined.q-field--readonly .q-field__control:before {
border-style: solid;
}
</style>
<i18n>
es:
Cancel: Cancelar
</i18n>

View File

@ -200,16 +200,10 @@ onUnmounted(() => (stateStore.rightDrawer = false));
<VnInputDate <VnInputDate
:label="t('lastEntries.since')" :label="t('lastEntries.since')"
dense dense
emit-date-format
v-model="from" v-model="from"
class="q-mr-lg" class="q-mr-lg"
/> />
<VnInputDate <VnInputDate :label="t('lastEntries.to')" dense v-model="to" />
:label="t('lastEntries.to')"
dense
emit-date-format
v-model="to"
/>
</div> </div>
<QSpace /> <QSpace />
<div id="st-actions"></div> <div id="st-actions"></div>

View File

@ -192,7 +192,6 @@ const decrement = (paramsObj, key) => {
v-model="params.from" v-model="params.from"
@update:model-value="searchFn()" @update:model-value="searchFn()"
is-outlined is-outlined
emit-date-format
/> />
</QItemSection> </QItemSection>
</QItem> </QItem>
@ -203,7 +202,6 @@ const decrement = (paramsObj, key) => {
v-model="params.to" v-model="params.to"
@update:model-value="searchFn()" @update:model-value="searchFn()"
is-outlined is-outlined
emit-date-format
/> />
</QItemSection> </QItemSection>
</QItem> </QItem>

View File

@ -169,7 +169,6 @@ const columns = computed(() => [
<VnInputDate <VnInputDate
:label="t('salesClientsTable.from')" :label="t('salesClientsTable.from')"
dense dense
emit-date-format
v-model="from" v-model="from"
class="q-mr-lg" class="q-mr-lg"
style="width: 150px" style="width: 150px"
@ -177,7 +176,6 @@ const columns = computed(() => [
<VnInputDate <VnInputDate
:label="t('salesClientsTable.to')" :label="t('salesClientsTable.to')"
dense dense
emit-date-format
v-model="to" v-model="to"
style="width: 150px" style="width: 150px"
/> />

View File

@ -17,15 +17,6 @@ const props = defineProps({
}, },
}); });
const from = Date.vnNew();
Outdated
Review

Estos filtros ya están en TicketList, no creo que haga falta que estén duplicados

Estos filtros ya están en TicketList, no creo que haga falta que estén duplicados
const to = Date.vnNew();
to.setDate(to.getDate() + 1);
const defaultParams = {
from: toDateString(from),
to: toDateString(to),
};
const workers = ref(); const workers = ref();
const provinces = ref(); const provinces = ref();
const states = ref(); const states = ref();
@ -44,11 +35,7 @@ const warehouses = ref();
@on-fetch="(data) => (workers = data)" @on-fetch="(data) => (workers = data)"
auto-load auto-load
/> />
<VnFilterPanel <VnFilterPanel :data-key="props.dataKey" :search-button="true">
:data-key="props.dataKey"
:params="defaultParams"
:search-button="true"
>
<template #tags="{ tag, formatFn }"> <template #tags="{ tag, formatFn }">
<div class="q-gutter-x-xs"> <div class="q-gutter-x-xs">
<strong>{{ t(`params.${tag.label}`) }}: </strong> <strong>{{ t(`params.${tag.label}`) }}: </strong>

View File

@ -26,8 +26,8 @@ const to = Date.vnNew();
to.setDate(to.getDate() + 1); to.setDate(to.getDate() + 1);
const userParams = { const userParams = {
Outdated
Review

(Aquí)

(Aquí)
from: toDateString(from), from: from.toISOString(),
to: toDateString(to), to: to.toISOString(),
}; };
function navigate(id) { function navigate(id) {

View File

@ -74,7 +74,7 @@ const agencyOptions = ref([]);
type="number" type="number"
min="0" min="0"
/> />
<VnInputTime v-model="data.hour" :label="t('Closing')" clearable /> <VnInputTime v-model="data.hour" :label="t('Closing')" />
Review

Ahora el componente por defecto es clearable

Ahora el componente por defecto es clearable
alexm marked this conversation as resolved
Review

Teniendo valor en el VnInputTime, no aparece la X de clearable

Teniendo valor en el VnInputTime, no aparece la X de clearable
</VnRow> </VnRow>
<VnRow class="row q-gutter-md q-mb-md"> <VnRow class="row q-gutter-md q-mb-md">