- corretto componente CDateTime
- aggiunto componente CDateTimeStartEnd
This commit is contained in:
363
src/components/CDateTimeStartEnd/CDateTimeStartEnd.ts
Executable file
363
src/components/CDateTimeStartEnd/CDateTimeStartEnd.ts
Executable file
@@ -0,0 +1,363 @@
|
||||
import { defineComponent, ref, watch, computed } from 'vue';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { tools } from '@src/store/Modules/tools';
|
||||
import { toolsext } from '@store/Modules/toolsext';
|
||||
|
||||
function toTS(s: string | null): number | null {
|
||||
if (!s) return null;
|
||||
const iso = s.includes('T') ? s : s.replace(' ', 'T');
|
||||
const ts = Date.parse(iso);
|
||||
return Number.isNaN(ts) ? null : ts;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CDateTimeStartEnd',
|
||||
emits: [
|
||||
'update:startValue',
|
||||
'update:endValue',
|
||||
'show',
|
||||
'savetoclose',
|
||||
'clear-start',
|
||||
'clear-end',
|
||||
],
|
||||
props: {
|
||||
startValue: { type: [String, null] as unknown as () => string | null, default: null },
|
||||
endValue: { type: [String, null] as unknown as () => string | null, default: null },
|
||||
|
||||
startLabel: { type: String, default: 'Inizio' },
|
||||
endLabel: { type: String, default: 'Fine' },
|
||||
|
||||
data_class: { type: String, default: '' },
|
||||
canEdit: { type: Boolean, default: true },
|
||||
disable: { type: Boolean, default: false },
|
||||
bgcolor: { type: String, default: '' },
|
||||
dense: { type: Boolean, default: false },
|
||||
|
||||
view: { type: String as () => 'date-time' | 'date' | 'time', default: 'date-time' },
|
||||
nullableStart: { type: Boolean, default: true },
|
||||
nullableEnd: { type: Boolean, default: true },
|
||||
nullText: { type: String, default: '—' },
|
||||
|
||||
calendarIcon: { type: String, default: 'fas fa-calendar-day' },
|
||||
clockIcon: { type: String, default: 'fas fa-clock' },
|
||||
clearIcon: { type: String, default: 'fas fa-ban' },
|
||||
|
||||
optionalText: { type: String, default: 'opzionale' },
|
||||
enableEndText: { type: String, default: 'Attiva' },
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const $q = useQuasar();
|
||||
const { t } = useI18n();
|
||||
|
||||
const isMobile = computed(() => $q.screen.lt.sm);
|
||||
|
||||
// local state
|
||||
const startVal = ref<string | null>(null);
|
||||
const endVal = ref<string | null>(null);
|
||||
const startPrev = ref<string | null>(null);
|
||||
const endPrev = ref<string | null>(null);
|
||||
|
||||
// dialog states
|
||||
const startDateDialog = ref(false);
|
||||
const startTimeDialog = ref(false);
|
||||
const endDateDialog = ref(false);
|
||||
const endTimeDialog = ref(false);
|
||||
|
||||
const startDateDesktopDialog = ref(false);
|
||||
const startTimeDesktopDialog = ref(false);
|
||||
const endDateDesktopDialog = ref(false);
|
||||
const endTimeDesktopDialog = ref(false);
|
||||
|
||||
const endError = ref<string>('');
|
||||
|
||||
// sync props -> local
|
||||
watch(
|
||||
() => props.startValue,
|
||||
(v) => {
|
||||
startVal.value = v ? tools.getstrYYMMDDDateTime(v) : null;
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
watch(
|
||||
() => props.endValue,
|
||||
(v) => {
|
||||
endVal.value = v ? tools.getstrYYMMDDDateTime(v) : null;
|
||||
validateRange();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
function getstrDate(val: string | Date | null) {
|
||||
if (!val) return props.nullText;
|
||||
if (props.view === 'date-time') return tools.getstrDateTime(val);
|
||||
if (props.view === 'date') return tools.getstrDate(val);
|
||||
return tools.getstrTime(val);
|
||||
}
|
||||
|
||||
function opening(kind: 'start' | 'end') {
|
||||
if (kind === 'start') {
|
||||
startPrev.value = startVal.value;
|
||||
if (!startVal.value) startVal.value = tools.getstrYYMMDDDateTime(new Date());
|
||||
} else {
|
||||
endPrev.value = endVal.value;
|
||||
if (!endVal.value)
|
||||
endVal.value = startVal.value || tools.getstrYYMMDDDateTime(new Date());
|
||||
}
|
||||
endError.value = '';
|
||||
emit('show');
|
||||
}
|
||||
|
||||
function cancelStart() {
|
||||
startVal.value = startPrev.value;
|
||||
closeAllStart();
|
||||
}
|
||||
function cancelEnd() {
|
||||
endVal.value = endPrev.value;
|
||||
endError.value = '';
|
||||
closeAllEnd();
|
||||
}
|
||||
|
||||
function saveStart() {
|
||||
const currStart = startVal.value;
|
||||
const prevStart = startPrev.value;
|
||||
|
||||
// salva start
|
||||
emit('update:startValue', currStart as unknown as string);
|
||||
emit('savetoclose', { which: 'start', current: currStart, prev: prevStart });
|
||||
|
||||
// Se esiste end e l'intervallo è invalido, riallinea fine preservando l'ora originale
|
||||
if (endVal.value) {
|
||||
const tsStart = toTS(currStart);
|
||||
const tsEnd = toTS(endVal.value);
|
||||
if (tsStart !== null && tsEnd !== null && tsEnd < tsStart) {
|
||||
endVal.value = buildEndWithStartDatePreservingEndTime(tsStart, tsEnd);
|
||||
endError.value = '';
|
||||
emit('update:endValue', endVal.value as unknown as string);
|
||||
emit('savetoclose', { which: 'end', current: endVal.value, prev: endPrev.value });
|
||||
tools.showNeutralNotif(
|
||||
$q,
|
||||
t('date.rangeFixed') || 'Fine allineata all’inizio mantenendo l’ora originale'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
closeAllStart();
|
||||
}
|
||||
|
||||
/**
|
||||
* Costruisce una nuova data di fine usando:
|
||||
* - data (Y/M/D) = quella di start
|
||||
* - ora (h:m:s:ms) = quella di end originale
|
||||
* Se il risultato è ancora < start, sposta end al giorno successivo mantenendo la stessa ora.
|
||||
*/
|
||||
function buildEndWithStartDatePreservingEndTime(
|
||||
tsStart: number,
|
||||
tsEnd: number
|
||||
): string {
|
||||
// Se il componente è solo 'date', non c'è ora da preservare: end = start
|
||||
if (props.view === 'date') {
|
||||
return tools.getstrYYMMDDDateTime(new Date(tsStart));
|
||||
}
|
||||
|
||||
const dStart = new Date(tsStart);
|
||||
const dEnd = new Date(tsEnd);
|
||||
|
||||
// Ricostruisci end: data = start, ora = end originale
|
||||
const newEnd = new Date(
|
||||
dStart.getFullYear(),
|
||||
dStart.getMonth(),
|
||||
dStart.getDate(),
|
||||
dEnd.getHours(),
|
||||
dEnd.getMinutes(),
|
||||
dEnd.getSeconds(),
|
||||
dEnd.getMilliseconds()
|
||||
);
|
||||
|
||||
// Se così è ancora < start (es. end 08:00, start 10:00 stesso giorno) → bump di 1 giorno
|
||||
if (newEnd.getTime() < tsStart) {
|
||||
newEnd.setDate(newEnd.getDate() + 1);
|
||||
}
|
||||
|
||||
return tools.getstrYYMMDDDateTime(newEnd);
|
||||
}
|
||||
|
||||
function saveEnd() {
|
||||
if (!validateRange(true)) {
|
||||
// blocco il salvataggio se ancora invalida
|
||||
return;
|
||||
}
|
||||
emit('update:endValue', endVal.value as unknown as string);
|
||||
emit('savetoclose', { which: 'end', current: endVal.value, prev: endPrev.value });
|
||||
closeAllEnd();
|
||||
}
|
||||
|
||||
function onStartChange(v: string | Date) {
|
||||
startVal.value = typeof v === 'string' ? v : tools.getstrYYMMDDDateTime(v);
|
||||
}
|
||||
function onEndChange(v: string | Date) {
|
||||
endVal.value = typeof v === 'string' ? v : tools.getstrYYMMDDDateTime(v);
|
||||
validateRange();
|
||||
}
|
||||
|
||||
function clearStart() {
|
||||
startVal.value = null;
|
||||
emit('update:startValue', null as unknown as string);
|
||||
emit('savetoclose', { which: 'start', current: null, prev: startPrev.value });
|
||||
// se non c'è inizio, rimuovo anche fine per coerenza
|
||||
if (endVal.value) {
|
||||
endVal.value = null;
|
||||
emit('update:endValue', null as unknown as string);
|
||||
emit('savetoclose', { which: 'end', current: null, prev: endPrev.value });
|
||||
}
|
||||
emit('clear-start');
|
||||
tools.showNeutralNotif($q, t('common.cleared') || 'Valore rimosso');
|
||||
closeAllStart();
|
||||
}
|
||||
function clearEnd() {
|
||||
endVal.value = null;
|
||||
emit('update:endValue', null as unknown as string);
|
||||
emit('clear-end');
|
||||
tools.showNeutralNotif($q, t('common.cleared') || 'Valore rimosso');
|
||||
endError.value = '';
|
||||
closeAllEnd();
|
||||
}
|
||||
function enableEnd() {
|
||||
endVal.value = startVal.value || tools.getstrYYMMDDDateTime(new Date());
|
||||
validateRange(true);
|
||||
emit('update:endValue', endVal.value as unknown as string);
|
||||
}
|
||||
|
||||
function openStartDate() {
|
||||
opening('start');
|
||||
if (isMobile.value) startDateDialog.value = true;
|
||||
else startDateDesktopDialog.value = true;
|
||||
}
|
||||
function openStartTime() {
|
||||
opening('start');
|
||||
if (isMobile.value) startTimeDialog.value = true;
|
||||
else startTimeDesktopDialog.value = true;
|
||||
}
|
||||
function openEndDate() {
|
||||
opening('end');
|
||||
if (isMobile.value) endDateDialog.value = true;
|
||||
else endDateDesktopDialog.value = true;
|
||||
}
|
||||
function openEndTime() {
|
||||
opening('end');
|
||||
if (isMobile.value) endTimeDialog.value = true;
|
||||
else endTimeDesktopDialog.value = true;
|
||||
}
|
||||
|
||||
function closeAllStart() {
|
||||
startDateDialog.value = false;
|
||||
startTimeDialog.value = false;
|
||||
startDateDesktopDialog.value = false;
|
||||
startTimeDesktopDialog.value = false;
|
||||
}
|
||||
function closeAllEnd() {
|
||||
endDateDialog.value = false;
|
||||
endTimeDialog.value = false;
|
||||
endDateDesktopDialog.value = false;
|
||||
endTimeDesktopDialog.value = false;
|
||||
}
|
||||
|
||||
function validateRange(showMsg = false): boolean {
|
||||
endError.value = '';
|
||||
const tsStart = toTS(startVal.value);
|
||||
const tsEnd = toTS(endVal.value);
|
||||
if (tsStart != null && tsEnd != null && tsEnd < tsStart) {
|
||||
endError.value =
|
||||
t('date.invalidRange') || 'La data di fine non può precedere l’inizio';
|
||||
if (showMsg) tools.showNeutralNotif($q, endError.value);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const confirmLabelStartDate = computed(() =>
|
||||
isMobile.value
|
||||
? t('common.set') || 'Imposta'
|
||||
: `Imposta a ${tools.getstrDateLong(startVal.value)}`
|
||||
);
|
||||
const confirmLabelStartTime = computed(() =>
|
||||
isMobile.value
|
||||
? t('common.set') || 'Imposta'
|
||||
: `Imposta a ${tools.getstrTime(startVal.value)}`
|
||||
);
|
||||
const confirmLabelEndDate = computed(() =>
|
||||
isMobile.value
|
||||
? t('common.set') || 'Imposta'
|
||||
: `Imposta a ${tools.getstrDateLong(endVal.value)}`
|
||||
);
|
||||
const confirmLabelEndTime = computed(() =>
|
||||
isMobile.value
|
||||
? t('common.set') || 'Imposta'
|
||||
: `Imposta a ${tools.getstrTime(endVal.value)}`
|
||||
);
|
||||
|
||||
const canEditEnd = computed(() => !!endVal.value || !props.nullableEnd);
|
||||
|
||||
return {
|
||||
toolsext,
|
||||
tools,
|
||||
// values & labels
|
||||
startVal,
|
||||
endVal,
|
||||
startLabel: computed(() => props.startLabel),
|
||||
endLabel: computed(() => props.endLabel),
|
||||
|
||||
// ui flags
|
||||
dense: props.dense,
|
||||
bgcolor: props.bgcolor,
|
||||
disable: props.disable,
|
||||
data_class: props.data_class,
|
||||
canEdit: props.canEdit,
|
||||
nullableStart: props.nullableStart,
|
||||
nullableEnd: props.nullableEnd,
|
||||
nullText: props.nullText,
|
||||
optionalText: props.optionalText,
|
||||
enableEndText: props.enableEndText,
|
||||
|
||||
calendarIcon: props.calendarIcon,
|
||||
clockIcon: props.clockIcon,
|
||||
clearIcon: props.clearIcon,
|
||||
|
||||
// dialogs
|
||||
startDateDialog,
|
||||
startTimeDialog,
|
||||
endDateDialog,
|
||||
endTimeDialog,
|
||||
startDateDesktopDialog,
|
||||
startTimeDesktopDialog,
|
||||
endDateDesktopDialog,
|
||||
endTimeDesktopDialog,
|
||||
|
||||
// methods
|
||||
getstrDate,
|
||||
openStartDate,
|
||||
openStartTime,
|
||||
openEndDate,
|
||||
openEndTime,
|
||||
cancelStart,
|
||||
cancelEnd,
|
||||
saveStart,
|
||||
saveEnd,
|
||||
onStartChange,
|
||||
onEndChange,
|
||||
clearStart,
|
||||
clearEnd,
|
||||
enableEnd,
|
||||
|
||||
// helpers
|
||||
confirmLabelStartDate,
|
||||
confirmLabelStartTime,
|
||||
confirmLabelEndDate,
|
||||
confirmLabelEndTime,
|
||||
isMobile,
|
||||
canEditEnd,
|
||||
|
||||
endError,
|
||||
};
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user