Files
web/app/components/datepicker/PickDay.vue
2026-05-26 13:49:31 +07:00

290 lines
7.5 KiB
Vue

<template>
<div class="is-flex is-flex-direction-column is-gap-1">
<div class="is-flex is-gap-1 is-justify-content-space-between h-8">
<div class="buttons is-gap-0.5 m-0 is-align-items-stretch">
<button
@click="previousYear"
class="button is-text rounded-full"
>
<span class="icon">
<Icon
name="ic:baseline-keyboard-double-arrow-left"
:size="24"
/>
</span>
</button>
<button
v-if="type === 'days'"
@click="previousMonth"
class="button is-text rounded-full"
>
<span class="icon">
<Icon
name="ic:baseline-keyboard-arrow-left"
:size="24"
/>
</span>
</button>
</div>
<div class="buttons is-gap-0 m-0 is-align-items-center">
<button
v-if="type === 'days'"
@click="type = 'months'"
class="button is-white p-1.5 fs-14 has-text-primary font-semibold h-full"
>
T{{ month }}
</button>
<button
@click="type = 'years'"
class="button is-white p-1.5 fs-14 has-text-primary font-semibold h-full"
>
{{ caption || year }}
</button>
</div>
<div class="buttons is-gap-0.5 m-0 is-align-items-stretch">
<button
class="button is-text rounded-full"
@click="nextMonth"
v-if="type === 'days'"
>
<span class="icon">
<Icon
name="ic:baseline-keyboard-arrow-right"
:size="24"
/>
</span>
</button>
<button
@click="nextYear"
class="button is-text rounded-full"
>
<span class="icon">
<Icon
name="ic:baseline-keyboard-double-arrow-right"
:size="24"
/>
</span>
</button>
</div>
</div>
<div v-if="type === 'days'">
<div class="fixed-grid has-7-cols mb-1 fs-13 has-background-white-bis has-text-grey rounded-sm">
<div class="grid is-gap-0">
<p
v-for="(m, h) in dateOfWeek"
:key="h"
class="cell py-1 is-flex is-justify-content-center is-align-items-center is-unselectable"
>
{{ m.text }}
</p>
</div>
</div>
<div
v-for="(v, i) in weeks"
:key="i"
class="fixed-grid has-7-cols mb-0"
>
<div class="grid is-gap-0">
<div
v-for="(m, h) in v.dates"
:key="h"
class="cell is-flex is-justify-content-center is-align-items-center"
style="height: 32px"
>
<span
v-if="m.disabled"
class="fs-13 has-text-grey-light is-unselectable"
>
{{ m.dayPrint.replace(/^0/, "") }}
</span>
<button
v-else
@click="choose(m)"
style="width: 26px; height: 26px"
:class="[
'button fs-13 font-normal p-1 rounded-full is-flex',
{
'is-white': m.date !== $dayjs(today).format('YYYY/MM/DD'),
'is-primary': m.date === curdate,
'has-text-grey': m.currentMonth !== m.monthCondition && m.date !== curdate,
},
]"
>
{{ m.dayPrint.replace(/^0/, "") }}
</button>
</div>
</div>
</div>
<hr class="my-2" />
<div class="mx-2 is-flex is-gap-0.5 is-align-items-center">
<p class="fs-14">Hôm nay:</p>
<button
@click="chooseToday"
class="button is-white has-text-primary-65 p-1 fs-14"
>
{{ $dayjs(today).format("L") }}
</button>
</div>
</div>
<PickMonth
v-else-if="type === 'months'"
@month="selectMonth"
/>
<PickYear
v-else-if="type === 'years'"
v-bind="{ year, month, action }"
@year="selectYear"
@caption="changeCaption"
/>
</div>
</template>
<script setup>
import PickMonth from "@/components/datepicker/PickMonth.vue";
import PickYear from "@/components/datepicker/PickYear.vue";
const { $id, $dayjs, $unique } = useNuxtApp();
const emit = defineEmits(["date"]);
const props = defineProps({
date: String,
maxdate: [Date, String],
});
const dates = ref([]);
const dateOfWeek = [
{ id: 0, text: "T2" },
{ id: 2, text: "T3" },
{ id: 2, text: "T4" },
{ id: 3, text: "T5" },
{ id: 4, text: "T6" },
{ id: 5, text: "T7" },
{ id: 6, text: "CN" },
];
const weeks = ref([]);
const year = ref();
const month = ref();
const type = ref("days");
const caption = ref();
const action = ref();
const curdate = ref();
const today = new Date();
function showDate() {
curdate.value = props.date;
year.value = $dayjs(curdate.value || today).year();
month.value = $dayjs(curdate.value || today).month() + 1;
getDates();
}
function chooseToday() {
emit("date", $dayjs(today).format("YYYY/MM/DD"));
year.value = $dayjs(today).year();
month.value = $dayjs(today).month() + 1;
getDates();
}
function changeCaption(v) {
caption.value = v;
}
function selectMonth(v) {
month.value = v;
getDates();
type.value = "days";
}
function selectYear(v) {
year.value = v;
getDates();
type.value = "days";
}
function getDates() {
caption.value = undefined;
dates.value = allDaysInMonth(year.value, month.value);
weeks.value = $unique(dates.value, ["week"]).map((v) => {
return { week: v.week };
});
weeks.value.forEach((v) => {
v.dates = dates.value.filter((x) => x.week === v.week);
});
}
function nextMonth() {
month.value = month.value + 1;
if (month.value > 12) {
month.value = 1;
year.value += 1;
}
getDates();
}
function previousMonth() {
month.value = month.value - 1;
if (month.value === 0) {
month.value = 12;
year.value -= 1;
}
getDates();
}
function nextYear() {
if (type.value === "years") return (action.value = { name: "next", id: $id() });
year.value += 1;
getDates();
}
function previousYear() {
if (type.value === "years") return (action.value = { name: "previous", id: $id() });
year.value -= 1;
getDates();
}
function choose(m) {
emit("date", m.date);
}
function allDaysInMonth(year, month) {
const days = Array.from({ length: $dayjs([year, month, 1]).daysInMonth() }, (_, i) => i + 1);
const arr = [];
days.forEach((day) => {
for (let i = 0; i < 7; i++) {
const thedate = $dayjs([year, month, day]).weekday(i);
const date = thedate.format("YYYY/MM/DD");
const monthCondition = thedate.month() + 1;
const found = arr.find((x) => x.date === date);
if (!found) {
let disabled = false;
if (props.maxdate) {
const maxDateObj = $dayjs(props.maxdate);
if (maxDateObj.diff(props.date, "day") >= 0 && maxDateObj.startOf("day").diff(date, "day") < 0) {
disabled = true;
}
}
const ele = {
date,
day,
week: thedate.week(),
dayOfWeek: thedate.day(),
dayPrint: thedate.format("DD"),
monthCondition,
currentMonth: month,
disabled,
};
arr.push(ele);
}
}
});
return arr;
}
watch(
() => props.date,
() => {
showDate();
},
{ immediate: true },
);
</script>
<style scoped>
a {
color: var(--bulma-link-60);
}
.control.has-icons-left .icon,
.control.has-icons-right .icon {
width: 100%;
height: 100%;
}
</style>