This commit is contained in:
2022-11-14 11:56:21 +08:00
commit 0a63adba99
337 changed files with 25661 additions and 0 deletions

View File

@@ -0,0 +1,148 @@
<template>
<div class="layui-laydate">
<div class="layui-laydate-main laydate-main-list-0">
<div class="layui-laydate-header">
<i
class="layui-icon laydate-icon laydate-prev-y"
@click="changeYearOrMonth('year', -1)"
></i
>
<i
class="layui-icon laydate-icon laydate-prev-m"
@click="changeYearOrMonth('month', -1)"
></i
>
<div class="laydate-set-ym">
<span @click="datePicker.showPanel.value = 'year'"
>{{ datePicker.currentYear.value }} {{ t("datePicker.year") }}</span
>
<span @click="datePicker.showPanel.value = 'month'">
{{ MONTH_NAME[datePicker.currentMonth.value] }}
</span>
</div>
<i
class="layui-icon laydate-icon laydate-next-m"
@click="changeYearOrMonth('month', 1)"
></i
>
<i
class="layui-icon laydate-icon laydate-next-y"
@click="changeYearOrMonth('year', 1)"
></i
>
</div>
<DateContent
:date-list="dateList"
v-model="Day"
@simple="footOnOk"
@update:model-value="ChildUpdateModelValue"
></DateContent>
<PanelFoot @ok="footOnOk" @now="footOnNow" @clear="footOnClear">
<span
v-if="datePicker.type === 'datetime'"
@click="datePicker.showPanel.value = 'time'"
class="laydate-btns-time"
>{{ t("datePicker.selectTime") }}</span
>
</PanelFoot>
</div>
</div>
</template>
<script lang="ts">
export default {
name: "DatePanel",
};
</script>
<script lang="ts" setup>
import { computed, inject, ref, watch } from "vue";
import { provideType } from "../interface";
import { setDateList } from "../day";
import PanelFoot from "./PanelFoot.vue";
import DateContent from "./components/DateContent.vue";
import dayjs from "dayjs";
import { useI18n } from "../../../language";
export interface TimePanelProps {
modelValue: number;
}
const { t } = useI18n();
const props = withDefaults(defineProps<TimePanelProps>(), {});
const emits = defineEmits(["update:modelValue", "ok"]);
const Day = ref(props.modelValue);
const datePicker: provideType = inject("datePicker") as provideType;
const dateList = ref<any>([]);
const MONTH_NAME = computed(() => [
t("datePicker.january"),
t("datePicker.february"),
t("datePicker.march"),
t("datePicker.april"),
t("datePicker.may"),
t("datePicker.june"),
t("datePicker.july"),
t("datePicker.august"),
t("datePicker.september"),
t("datePicker.october"),
t("datePicker.november"),
t("datePicker.december"),
]);
// 监听年月, 刷新日期
watch(
[datePicker.currentYear, datePicker.currentMonth],
() => {
dateList.value = setDateList(
datePicker.currentYear.value,
datePicker.currentMonth.value
);
},
{ immediate: true }
);
watch(
() => props.modelValue,
() => {
Day.value = props.modelValue;
}
);
// 切换年月
const changeYearOrMonth = (type: "year" | "month", num: number) => {
if (type === "year") {
datePicker.currentYear.value += num;
} else {
let month = datePicker.currentMonth.value + num;
if (month > 11) {
month = 0;
datePicker.currentYear.value++;
} else if (month < 0) {
month = 11;
datePicker.currentYear.value--;
}
datePicker.currentMonth.value = month;
}
};
//关闭回调
const footOnOk = () => {
emits("update:modelValue", Day.value);
datePicker.ok();
};
//现在回调
const footOnNow = () => {
datePicker.currentYear.value = dayjs().year();
datePicker.currentMonth.value = dayjs().month();
Day.value = new Date(new Date().toDateString()).getTime();
};
//清空回调
const footOnClear = () => {
Day.value = -1;
};
const ChildUpdateModelValue = () => {
emits("update:modelValue", Day.value);
};
</script>

View File

@@ -0,0 +1,350 @@
<template>
<div
class="layui-laydate layui-laydate-range"
:class="'layui-laydate-range-' + datePicker.showPanel.value"
>
<div style="display: flex">
<div class="layui-laydate-main laydate-main-list-0">
<div class="layui-laydate-header">
<i
class="layui-icon laydate-icon laydate-prev-y"
@click="changeYearOrMonth('year', -1)"
></i
>
<i
class="layui-icon laydate-icon laydate-prev-m"
@click="changeYearOrMonth('month', -1)"
></i
>
<div class="laydate-set-ym">
<lay-dropdown ref="dropdownYearPanelRefLeft">
<span class="laydate-range-time"
>{{ startTime.year || "--" }} {{ t("datePicker.year") }}</span
>
<template #content>
<YearPanel
class="time-panel"
v-model="startTime.year"
@ok="closeTimePanel"
></YearPanel>
</template>
</lay-dropdown>
<lay-dropdown ref="dropdownMonthPanelRefLeft">
<span class="laydate-range-time">
{{ MONTH_NAME[startTime.month] }}</span
>
<template #content>
<MonthPanel
class="time-panel"
v-model="startTime.month"
@ok="closeTimePanel"
></MonthPanel>
</template>
</lay-dropdown>
<lay-dropdown
ref="dropdownTimePanelRefLeft"
v-if="datePicker.type === 'datetime'"
>
<span class="laydate-range-time">
{{
dayjs()
.hour(startTime.hms.hh)
.minute(startTime.hms.mm)
.second(startTime.hms.ss)
.format("HH:mm:ss")
}}
</span>
<template #content>
<TimePanel
v-model="startTime.hms"
class="time-panel"
@ok="closeTimePanel"
></TimePanel>
</template>
</lay-dropdown>
</div>
</div>
<DateContent
:date-list="prevDateList"
v-model:hoverDate="hoverDate"
v-model:startDate="startTime.day"
v-model:endDate="endTime.day"
></DateContent>
</div>
<div class="layui-laydate-main laydate-main-list-0">
<div class="layui-laydate-header">
<div class="laydate-set-ym">
<lay-dropdown ref="dropdownYearPanelRefRight">
<span class="laydate-range-time"
>{{
startTime.month + 1 > 11 ? startTime.year + 1 : startTime.year
}}
{{ t("datePicker.year") }}</span
>
<template #content>
<YearPanel
class="time-panel"
v-model="endTime.year"
@ok="closeRightYearPanel"
></YearPanel>
</template>
</lay-dropdown>
<lay-dropdown ref="dropdownMonthPanelRefRight">
<span class="laydate-range-time">
{{
MONTH_NAME[
startTime.month + 1 > 11
? startTime.month + 1 - 12
: startTime.month + 1
]
}}
</span>
<template #content>
<MonthPanel
class="time-panel"
v-model="endTime.month"
@ok="closeRightMonthPanel"
></MonthPanel>
</template>
</lay-dropdown>
<lay-dropdown
ref="dropdownTimePanelRefRight"
v-if="datePicker.type === 'datetime'"
>
<span class="laydate-range-time">
{{
dayjs()
.hour(endTime.hms.hh)
.minute(endTime.hms.mm)
.second(endTime.hms.ss)
.format("HH:mm:ss")
}}
</span>
<template #content>
<TimePanel
v-model="endTime.hms"
class="time-panel"
@ok="closeTimePanel"
></TimePanel>
</template>
</lay-dropdown>
</div>
<i
class="layui-icon laydate-icon laydate-next-m"
@click="changeYearOrMonth('month', 1)"
></i
>
<i
class="layui-icon laydate-icon laydate-next-y"
@click="changeYearOrMonth('year', 1)"
></i
>
</div>
<DateContent
:date-list="nextDateList"
v-model:hoverDate="hoverDate"
v-model:startDate="startTime.day"
v-model:endDate="endTime.day"
></DateContent>
</div>
</div>
<PanelFoot @ok="footOnOk" @now="footOnNow" @clear="footOnClear">
<span v-if="startTime.day !== -1" class="layui-laydate-preview">
{{ dayjs(startTime.day).format("YYYY-MM-DD") }}
<template v-if="datePicker.type === 'datetime'">
{{
dayjs()
.hour(startTime.hms.hh)
.minute(startTime.hms.mm)
.second(startTime.hms.ss)
.format("HH:mm:ss")
}}
</template>
{{ datePicker.rangeSeparator }}
<template v-if="endTime.day !== -1">
{{ dayjs(endTime.day).format("YYYY-MM-DD") }}
<template v-if="datePicker.type === 'datetime'">
{{
dayjs()
.hour(endTime.hms.hh)
.minute(endTime.hms.mm)
.second(endTime.hms.ss)
.format("HH:mm:ss")
}}
</template>
</template>
<template v-else> -- </template>
</span>
</PanelFoot>
</div>
</template>
<script lang="ts">
export default {
name: "DateRange",
};
</script>
<script lang="ts" setup>
import { computed, inject, reactive, ref, watch } from "vue";
import { provideType } from "../interface";
import { setDateList } from "../day";
import PanelFoot from "./PanelFoot.vue";
import DateContent from "./components/DateContent.vue";
import TimePanel from "./TimePanel.vue";
import YearPanel from "./YearPanel.vue";
import MonthPanel from "./MonthPanel.vue";
import LayDropdown from "../../dropdown/index.vue";
import dayjs from "dayjs";
import { useI18n } from "../../../language";
export interface DateRangeProps {
startTime: string;
endTime: string;
}
const props = withDefaults(defineProps<DateRangeProps>(), {});
const emits = defineEmits([
"update:modelValue",
"update:startTime",
"update:endTime",
]);
const datePicker: provideType = inject("datePicker") as provideType;
const { t } = useI18n();
const MONTH_NAME = computed(() => [
t("datePicker.january"),
t("datePicker.february"),
t("datePicker.march"),
t("datePicker.april"),
t("datePicker.may"),
t("datePicker.june"),
t("datePicker.july"),
t("datePicker.august"),
t("datePicker.september"),
t("datePicker.october"),
t("datePicker.november"),
t("datePicker.december"),
]);
const prevDateList = ref<any>([]);
const nextDateList = ref<any>([]);
const startTime = reactive({
year: props.startTime ? dayjs(props.startTime).year() : dayjs().year(),
month: props.startTime ? dayjs(props.startTime).month() : dayjs().month(),
day: props.startTime ? dayjs(props.startTime).startOf("day").valueOf() : -1,
hms: {
hh: props.startTime ? dayjs(props.startTime).hour() : 0,
mm: props.startTime ? dayjs(props.startTime).minute() : 0,
ss: props.startTime ? dayjs(props.startTime).second() : 0,
},
});
const endTime = reactive({
year: props.endTime ? dayjs(props.endTime).year() : dayjs().year(),
month: props.endTime ? dayjs(props.endTime).month() : dayjs().month(),
day: props.endTime ? dayjs(props.endTime).startOf("day").valueOf() : -1,
hms: {
hh: props.endTime ? dayjs(props.endTime).hour() : 0,
mm: props.endTime ? dayjs(props.endTime).minute() : 0,
ss: props.endTime ? dayjs(props.endTime).second() : 0,
},
});
const hoverDate = ref(-1);
// 切换年月
const changeYearOrMonth = (type: "year" | "month", num: number) => {
if (type === "year") {
startTime.year += num;
} else {
let month = startTime.month + num;
if (month > 11) {
month = 0;
startTime.year++;
} else if (month < 0) {
month = 11;
startTime.year--;
}
startTime.month = month;
}
};
// 监听年月, 刷新日期
watch(
() => [startTime.year, startTime.month],
() => {
prevDateList.value = setDateList(startTime.year, startTime.month);
nextDateList.value = setDateList(startTime.year, startTime.month + 1);
},
{ immediate: true }
);
//关闭选择时间的面板
const dropdownTimePanelRefLeft = ref();
const dropdownTimePanelRefRight = ref();
const dropdownYearPanelRefLeft = ref();
const dropdownYearPanelRefRight = ref();
const dropdownMonthPanelRefLeft = ref();
const dropdownMonthPanelRefRight = ref();
const closeTimePanel = () => {
if (dropdownTimePanelRefLeft.value) dropdownTimePanelRefLeft.value.hide();
if (dropdownTimePanelRefRight.value) dropdownTimePanelRefRight.value.hide();
if (dropdownYearPanelRefLeft.value) dropdownYearPanelRefLeft.value.hide();
if (dropdownMonthPanelRefLeft.value) dropdownMonthPanelRefLeft.value.hide();
};
const closeRightYearPanel = () => {
if (dropdownYearPanelRefRight.value) dropdownYearPanelRefRight.value.hide();
startTime.year = endTime.year;
};
const closeRightMonthPanel = () => {
dropdownMonthPanelRefRight.value.hide();
let month = endTime.month - 1;
if (month > 11) {
month = 0;
startTime.year++;
} else if (month < 0) {
month = 11;
startTime.year--;
}
startTime.month = month;
};
//关闭回调
const footOnOk = () => {
let format =
datePicker.type === "datetime" ? "YYYY-MM-DD HH:mm:ss" : "YYYY-MM-DD";
let startTimeVal =
startTime.day !== -1 && endTime.day !== -1
? dayjs(startTime.day)
.hour(startTime.hms.hh)
.minute(startTime.hms.mm)
.second(startTime.hms.ss)
.format(format)
: "";
let endTimeVal =
startTime.day !== -1 && endTime.day !== -1
? dayjs(endTime.day)
.hour(endTime.hms.hh)
.minute(endTime.hms.mm)
.second(endTime.hms.ss)
.format(format)
: "";
emits("update:startTime", startTimeVal);
emits("update:endTime", endTimeVal);
datePicker.ok();
};
//现在回调
const footOnNow = () => {
startTime.year = dayjs().year();
startTime.month = dayjs().month();
startTime.day = new Date(new Date().toDateString()).getTime();
startTime.hms.hh = dayjs().hour();
startTime.hms.mm = dayjs().minute();
startTime.hms.ss = dayjs().second();
endTime.day = -1;
};
//清空回调
const footOnClear = () => {
startTime.day = -1;
endTime.day = -1;
};
</script>

View File

@@ -0,0 +1,126 @@
<template>
<div class="layui-laydate">
<div class="layui-laydate-main laydate-main-list-0 laydate-ym-show">
<div class="layui-laydate-header">
<div class="laydate-set-ym">
<span @click="datePicker.showPanel.value = 'month'">{{
typeof Month !== "string"
? MONTH_NAME[Month]
: t("datePicker.selectMonth")
}}</span>
</div>
</div>
</div>
<div class="layui-laydate-content" style="height: 220px">
<ul class="layui-laydate-list laydate-month-list">
<li
v-for="item of MONTH_NAME"
:key="item"
:class="{ 'layui-this': MONTH_NAME.indexOf(item) === Month }"
@click="handleMonthClick(item)"
>
{{ item.slice(0, 3) }}
</li>
</ul>
</div>
<PanelFoot @ok="footOnOk" @now="footOnNow" @clear="footOnClear">
<span
v-if="datePicker.type === 'yearmonth'"
@click="datePicker.showPanel.value = 'year'"
class="laydate-btns-time"
>{{ t("datePicker.selectYear") }}</span
>
</PanelFoot>
</div>
</template>
<script lang="ts">
export default {
name: "TimePanel",
};
</script>
<script lang="ts" setup>
import dayjs from "dayjs";
import { useI18n } from "../../../language";
import { computed, inject, ref, watch } from "vue";
import { provideType } from "../interface";
import PanelFoot from "./PanelFoot.vue";
export interface TimePanelProps {
modelValue: number | string;
max?: number;
}
const props = withDefaults(defineProps<TimePanelProps>(), {
max: dayjs().year() + 100,
});
const emits = defineEmits(["update:modelValue", "ok"]);
const datePicker: provideType = inject("datePicker") as provideType;
const Month = ref(props.modelValue);
const { t } = useI18n();
const MONTH_NAME = computed(() => [
t("datePicker.january"),
t("datePicker.february"),
t("datePicker.march"),
t("datePicker.april"),
t("datePicker.may"),
t("datePicker.june"),
t("datePicker.july"),
t("datePicker.august"),
t("datePicker.september"),
t("datePicker.october"),
t("datePicker.november"),
t("datePicker.december"),
]);
// 点击月份
const handleMonthClick = (item: any) => {
Month.value = MONTH_NAME.value.indexOf(item);
if (!datePicker.range) {
if (datePicker.type === "yearmonth") {
datePicker.currentDay.value = dayjs(datePicker.currentDay.value)
.month(MONTH_NAME.value.indexOf(item))
.valueOf();
}
if (datePicker.type === "date" || datePicker.type === "datetime") {
emits("update:modelValue", MONTH_NAME.value.indexOf(item));
datePicker.showPanel.value = datePicker.type;
}
}
if (datePicker.simple) {
footOnOk();
}
};
watch(
() => props.modelValue,
() => {
Month.value = props.modelValue;
}
);
//关闭回调
const footOnOk = () => {
emits("update:modelValue", Month.value ? Month.value : -1);
if (datePicker.range) {
//关闭菜单
emits("ok");
return;
} else {
if (datePicker.type === "datetime" || datePicker.type === "date") {
datePicker.showPanel.value = datePicker.type;
} else {
datePicker.ok();
}
}
};
//现在回调
const footOnNow = () => {
Month.value = dayjs().month();
};
//清空回调
const footOnClear = () => {
Month.value = "";
};
</script>

View File

@@ -0,0 +1,295 @@
<template>
<div class="layui-laydate layui-laydate-range">
<div style="display: flex">
<div class="layui-laydate-main laydate-main-list-0">
<div class="layui-laydate-header">
<i
class="layui-icon laydate-icon laydate-prev-y"
@click="changeYear(-1)"
></i
>
<div class="laydate-set-ym">
<lay-dropdown ref="dropdownYearPanelRefLeft">
<span class="laydate-range-time"
>{{ startTime.year || "--" }} {{ t("datePicker.year") }}</span
>
<template #content>
<YearPanel
class="time-panel"
v-model="startTime.year"
@ok="closeLeftYearPanel"
></YearPanel>
</template>
</lay-dropdown>
</div>
</div>
<div class="layui-laydate-content" style="height: 220px">
<ul class="layui-laydate-list laydate-month-list">
<li
v-for="item of MONTH_NAME"
:key="item"
:data-unix="getUnix(item, 'left')"
:class="{
'layui-this':
getUnix(item, 'left') === startTime.unix ||
getUnix(item, 'left') === endTime.unix,
'laydate-range-hover': ifHasRangeHoverClass(
getUnix(item, 'left')
),
}"
@click="handleMonthClick(getUnix(item, 'left'))"
@mouseenter="monthItemMouseEnter($event, item)"
>
{{ item.slice(0, 3) }}
</li>
</ul>
</div>
</div>
<div class="layui-laydate-main laydate-main-list-0">
<div class="layui-laydate-header">
<div class="laydate-set-ym">
<lay-dropdown ref="dropdownYearPanelRefRight">
<span class="laydate-range-time"
>{{ startTime.year + 1 }} {{ t("datePicker.year") }}</span
>
<template #content>
<YearPanel
class="time-panel"
v-model="endTime.year"
@ok="closeRightYearPanel"
></YearPanel>
</template>
</lay-dropdown>
</div>
<i
class="layui-icon laydate-icon laydate-next-y"
@click="changeYear(1)"
></i
>
</div>
<div class="layui-laydate-content" style="height: 220px">
<ul class="layui-laydate-list laydate-month-list">
<li
v-for="item of MONTH_NAME"
:key="item"
:data-unix="getUnix(item, 'right')"
:class="{
'layui-this':
getUnix(item, 'right') === startTime.unix ||
getUnix(item, 'right') === endTime.unix,
'laydate-range-hover': ifHasRangeHoverClass(
getUnix(item, 'right')
),
}"
@click="handleMonthClick(getUnix(item, 'right'))"
@mouseenter="monthItemMouseEnter($event, item)"
>
{{ item.slice(0, 3) }}
</li>
</ul>
</div>
</div>
</div>
<PanelFoot @ok="footOnOk" @now="footOnNow" @clear="footOnClear">
<span v-if="startTime.unix !== -1" class="layui-laydate-preview">
{{ dayjs(startTime.unix).format("YYYY-MM-DD") }}
{{ datePicker.rangeSeparator }}
<template v-if="endTime.unix !== -1">
{{ dayjs(endTime.unix).format("YYYY-MM-DD") }}
</template>
<template v-else> -- </template>
</span>
</PanelFoot>
</div>
</template>
<script lang="ts">
export default {
name: "MonthRange",
};
</script>
<script lang="ts" setup>
import { inject, reactive, ref, watch, computed } from "vue";
import { provideType } from "../interface";
import PanelFoot from "./PanelFoot.vue";
import YearPanel from "./YearPanel.vue";
import dayjs from "dayjs";
import LayDropdown from "../../dropdown/index.vue";
import { useI18n } from "../../../language";
export interface DateRangeProps {
startTime: string;
endTime: string;
}
const props = withDefaults(defineProps<DateRangeProps>(), {});
const emits = defineEmits([
"update:modelValue",
"update:startTime",
"update:endTime",
]);
const { t } = useI18n();
const datePicker: provideType = inject("datePicker") as provideType;
const startTime = reactive({
year: props.startTime ? dayjs(props.startTime).year() : dayjs().year(),
unix: props.startTime
? dayjs(props.startTime).hour(0).minute(0).second(0).valueOf()
: -1,
});
const endTime = reactive({
year: props.endTime ? dayjs(props.endTime).year() : dayjs().year() + 1,
unix: props.startTime
? dayjs(props.endTime).hour(0).minute(0).second(0).valueOf()
: -1,
});
let hoverMonth = ref(-1);
const MONTH_NAME = computed(() => [
t("datePicker.january"),
t("datePicker.february"),
t("datePicker.march"),
t("datePicker.april"),
t("datePicker.may"),
t("datePicker.june"),
t("datePicker.july"),
t("datePicker.august"),
t("datePicker.september"),
t("datePicker.october"),
t("datePicker.november"),
t("datePicker.december"),
]);
// 切换年月
const changeYear = (num: number) => {
startTime.year += num;
};
// 点击月份
const handleMonthClick = (item: number) => {
if (startTime.unix === -1 && endTime.unix === -1) {
startTime.unix = item;
} else if (startTime.unix !== -1 && endTime.unix !== -1) {
hoverMonth.value = -1;
startTime.unix = item;
endTime.unix = -1;
} else if (startTime.unix !== -1 && endTime.unix === -1) {
endTime.unix = item;
if (item < startTime.unix) {
//swap
const first = startTime.unix;
const last = item;
startTime.unix = last;
endTime.unix = first;
}
}
};
// 监听年月, 刷新日期
watch(
() => [props.startTime, props.endTime],
() => {
startTime.year = props.startTime
? dayjs(props.startTime).year()
: dayjs().year();
startTime.unix = props.startTime
? dayjs(props.startTime).hour(0).minute(0).second(0).valueOf()
: -1;
endTime.year = props.endTime ? dayjs(props.endTime).year() : dayjs().year();
endTime.unix = props.startTime
? dayjs(props.endTime).hour(0).minute(0).second(0).valueOf()
: -1;
}
);
//关闭选择时间的面板
const dropdownYearPanelRefLeft = ref();
const dropdownYearPanelRefRight = ref();
const closeLeftYearPanel = () => {
if (dropdownYearPanelRefLeft.value) dropdownYearPanelRefLeft.value.hide();
};
const closeRightYearPanel = () => {
if (dropdownYearPanelRefRight.value) dropdownYearPanelRefRight.value.hide();
startTime.year = endTime.year;
};
//关闭回调
const footOnOk = () => {
let format = "YYYY-MM";
let startTimeVal =
startTime.unix !== -1 && endTime.unix !== -1
? dayjs(startTime.unix).format(format)
: "";
let endTimeVal =
endTime.unix !== -1 && endTime.unix !== -1
? dayjs(endTime.unix).format(format)
: "";
emits("update:startTime", startTimeVal);
emits("update:endTime", endTimeVal);
datePicker.ok();
};
//现在回调
const footOnNow = () => {
startTime.year = dayjs().year();
startTime.unix = dayjs(
startTime.year + "-" + (dayjs().month() + 1)
).valueOf();
endTime.unix = -1;
hoverMonth.value = -1;
};
//清空回调
const footOnClear = () => {
startTime.unix = -1;
endTime.unix = -1;
hoverMonth.value = -1;
};
const monthItemMouseEnter = (event: MouseEvent, item: any) => {
if (!datePicker.range) {
return;
}
if (startTime.unix === -1) {
return;
}
if (hoverMonth.value !== -1 && endTime.unix !== -1) {
hoverMonth.value = -1;
return;
}
hoverMonth.value = parseInt(
(event.target as HTMLElement).dataset.unix as string
);
};
const ifHasRangeHoverClass = computed(() => {
return function (item: any) {
if (!datePicker.range) {
return false;
}
if (startTime.unix === -1) {
return false;
}
if (hoverMonth.value === -1 && endTime.unix === -1) {
return false;
}
let hover = endTime.unix !== -1 ? endTime.unix : hoverMonth.value;
let max = startTime.unix > hover ? startTime.unix : hover;
let min = startTime.unix < hover ? startTime.unix : hover;
if (item >= min && item <= max) {
return true;
}
return false;
};
});
const getUnix = computed(() => {
return function (item: any, position: "left" | "right") {
let month = MONTH_NAME.value.indexOf(item);
let year = position === "left" ? startTime.year : startTime.year + 1;
return dayjs(year + "-" + (month + 1)).valueOf();
};
});
</script>

View File

@@ -0,0 +1,40 @@
<template>
<div class="layui-laydate-footer">
<slot></slot>
<div class="laydate-footer-btns">
<span lay-type="clear" class="laydate-btns-clear" @click="handelClear">{{
t("datePicker.clear")
}}</span>
<span lay-type="now" class="laydate-btns-now" @click="handelNow">{{
t("datePicker.now")
}}</span>
<span lay-type="confirm" class="laydate-btns-confirm" @click="handelOk">{{
t("datePicker.confirm")
}}</span>
</div>
</div>
</template>
<script lang="ts">
export default {
name: "PanelFoot",
};
</script>
<script lang="ts" setup>
import { useI18n } from "../../../language";
const { t } = useI18n();
const emits = defineEmits(["ok", "clear", "now"]);
const handelOk = () => {
emits("ok");
};
const handelNow = () => {
emits("now");
};
const handelClear = () => {
emits("clear");
};
</script>

View File

@@ -0,0 +1,164 @@
<template>
<div class="layui-laydate">
<div class="layui-laydate-main laydate-main-list-0 laydate-time-show">
<div class="layui-laydate-header">
<div class="laydate-set-ym">
<span class="laydate-time-text">{{
t("datePicker.selectTime")
}}</span>
</div>
</div>
<div class="layui-laydate-content" style="height: 210px">
<ul class="layui-laydate-list laydate-time-list" ref="timePanelRef">
<li
class="num-list"
v-for="item in els"
:key="item.type"
:data-type="item.type"
>
<ol class="scroll" @click="chooseTime">
<li
v-for="(it, index) in item.count"
:id="item.type + index.toString()"
:data-value="index.toString().padStart(2, '0')"
:data-type="item.type"
:key="it"
:class="['num', index == hms[item.type] ? 'layui-this' : '']"
>
{{ index.toString().padStart(2, "0") }}
</li>
</ol>
</li>
</ul>
</div>
</div>
<PanelFoot @ok="footOnOk" @now="footOnNow" @clear="footOnClear">
<span
v-if="datePicker.type === 'datetime' && !datePicker.range"
@click="datePicker.showPanel.value = 'datetime'"
class="laydate-btns-time"
>{{ t("datePicker.selectDate") }}</span
>
<template v-else-if="!isNaN(hms.hh) && !isNaN(hms.mm) && !isNaN(hms.ss)">
{{
dayjs().hour(hms.hh).minute(hms.mm).second(hms.ss).format("HH:mm:ss")
}}
</template>
</PanelFoot>
</div>
</template>
<script lang="ts">
export default {
name: "TimePanel",
};
</script>
<script lang="ts" setup>
import dayjs from "dayjs";
import { useI18n } from "../../../language";
import { inject, onMounted, ref, nextTick, watch } from "vue";
import { provideType } from "../interface";
import PanelFoot from "./PanelFoot.vue";
export interface hmsType {
hh: number;
mm: number;
ss: number;
[key: string]: any;
}
export interface TimePanelProps {
modelValue: hmsType;
}
const props = withDefaults(defineProps<TimePanelProps>(), {});
const emits = defineEmits(["update:modelValue", "ok"]);
const datePicker: provideType = inject("datePicker") as provideType;
const { t } = useI18n();
const els = [
{ count: 24, type: "hh" },
{ count: 60, type: "mm" },
{ count: 60, type: "ss" },
];
const hms = ref<hmsType>({
hh: props.modelValue.hh,
mm: props.modelValue.mm,
ss: props.modelValue.ss,
});
// 点击时间 - hms
const chooseTime = (e: any) => {
if (e.target.nodeName == "LI") {
let { value, type } = e.target.dataset;
hms.value[type as keyof typeof hms.value] = parseInt(value);
}
};
const timePanelRef = ref();
onMounted(() => {
scrollTo();
});
watch(
() => props.modelValue,
() => {
hms.value = {
hh: props.modelValue.hh,
mm: props.modelValue.mm,
ss: props.modelValue.ss,
};
},
{ deep: true }
);
const scrollTo = () => {
nextTick(() => {
timePanelRef.value.childNodes.forEach((element: HTMLElement) => {
if (element.nodeName === "LI") {
let scrollTop = 0;
let parentDom = element.firstElementChild as HTMLElement;
let childList = parentDom.childNodes;
for (let index = 0; index < childList.length; index++) {
const child = childList[index] as HTMLElement;
if (child.nodeName !== "LI") {
continue;
}
if (child.classList && child.classList.contains("layui-this")) {
scrollTop =
child.offsetTop -
(parentDom.offsetHeight - child.offsetHeight) / 2;
parentDom.scrollTo(0, scrollTop);
break;
}
}
}
});
});
};
//确认关闭回调
const footOnOk = () => {
emits("update:modelValue", hms.value);
if (datePicker.range) {
//关闭菜单
emits("ok");
return;
} else {
datePicker.ok();
if (datePicker.type === "datetime") {
datePicker.showPanel.value = "date";
}
}
};
//现在回调
const footOnNow = () => {
hms.value.hh = dayjs().hour();
hms.value.mm = dayjs().minute();
hms.value.ss = dayjs().second();
scrollTo();
};
//清空回调
const footOnClear = () => {
hms.value.hh = 0;
hms.value.mm = 0;
hms.value.ss = 0;
scrollTo();
};
</script>

View File

@@ -0,0 +1,146 @@
<template>
<div class="layui-laydate">
<div class="layui-laydate-main laydate-main-list-0 laydate-ym-show">
<div class="layui-laydate-header">
<div class="laydate-set-ym">
<span class="laydate-time-text">{{
t("datePicker.selectYear")
}}</span>
</div>
</div>
</div>
<div
class="layui-laydate-content"
style="height: 220px; overflow-y: auto"
ref="ScrollRef"
>
<ul class="layui-laydate-list laydate-year-list">
<li
v-for="item of yearList"
:key="item"
:class="{ 'layui-this': Year === item }"
@click="handleYearClick(item)"
>
{{ item }}
</li>
</ul>
</div>
<PanelFoot @ok="footOnOk" @now="footOnNow" @clear="footOnClear">
<span
v-if="datePicker.type === 'yearmonth'"
@click="datePicker.showPanel.value = 'month'"
class="laydate-btns-time"
>{{ t("datePicker.selectMonth") }}</span
>
<template v-else-if="Year > 0">{{ Year }}</template>
</PanelFoot>
</div>
</template>
<script lang="ts">
export default {
name: "YearPanel",
};
</script>
<script lang="ts" setup>
import dayjs from "dayjs";
import { useI18n } from "../../../language";
import { inject, nextTick, onMounted, ref, watch } from "vue";
import { getYears } from "../day";
import { provideType } from "../interface";
import PanelFoot from "./PanelFoot.vue";
export interface TimePanelProps {
modelValue: number | string;
max?: number;
}
const props = withDefaults(defineProps<TimePanelProps>(), {
max: dayjs().year() + 100,
});
const emits = defineEmits(["update:modelValue", "ok"]);
const datePicker: provideType = inject("datePicker") as provideType;
const yearList = ref<number[]>(getYears());
const unWatch = ref(false);
const Year = ref(props.modelValue);
const { t } = useI18n();
// 点击年份
const handleYearClick = (item: any) => {
unWatch.value = true;
Year.value = item;
if (!datePicker.range) {
if (datePicker.type === "year") {
datePicker.currentDay.value = dayjs().year(item).valueOf();
} else if (datePicker.type === "yearmonth") {
datePicker.currentDay.value = dayjs().year(item).valueOf();
datePicker.showPanel.value = "month";
emits("update:modelValue", Year.value);
} else {
emits("update:modelValue", Year.value);
datePicker.showPanel.value = datePicker.type;
}
}
setTimeout(() => {
unWatch.value = false;
}, 0);
if (datePicker.simple) {
footOnOk();
}
};
const ScrollRef = ref();
onMounted(() => {
scrollTo();
});
watch(
() => Year,
() => {
Year.value = props.modelValue;
}
);
const scrollTo = () => {
nextTick(() => {
let scrollTop = 0;
for (const child of ScrollRef.value.firstElementChild.childNodes) {
if (child.classList && child.classList.contains("layui-this")) {
scrollTop =
child.offsetTop -
(ScrollRef.value.offsetHeight - child.offsetHeight) / 2;
break;
}
}
ScrollRef.value.scrollTo(0, scrollTop);
});
};
//关闭回调
const footOnOk = () => {
emits("update:modelValue", Year.value ? Year.value : -1);
if (datePicker.range) {
//关闭菜单
emits("ok");
return;
} else {
if (datePicker.type === "datetime" || datePicker.type === "date") {
datePicker.showPanel.value = datePicker.type;
} else {
datePicker.ok();
}
}
};
//现在回调
const footOnNow = () => {
Year.value = dayjs().year();
if (datePicker.type === "yearmonth") {
datePicker.currentMonth.value = dayjs().month();
}
scrollTo();
};
//清空回调
const footOnClear = () => {
Year.value = "";
};
</script>

View File

@@ -0,0 +1,169 @@
<template>
<div class="layui-laydate-content">
<table style="width: 100%">
<thead>
<tr>
<th v-for="item of WEEK_NAME" :key="item">{{ item }}</th>
</tr>
</thead>
<tbody>
<template
v-for="(o, i) of dateList.length % 7 == 0
? dateList.length / 7
: Math.floor(dateList.length / 7) + 1"
:key="i"
>
<tr>
<td
v-for="(item, index) of dateList.slice(i * 7, i * 7 + 7)"
:key="index"
:data-unix="item.value"
:class="{
'laydate-day-prev': item.type !== 'current',
'layui-this':
item.value === modelValue ||
(datePicker.range &&
item.type === 'current' &&
(item.value == startDate || item.value == endDate)),
'laydate-range-hover': ifHasRangeHoverClass(item),
'layui-disabled': item.type !== 'current' && datePicker.range,
}"
@click="handleDayClick(item)"
@mouseenter="dayItemMouseEnter($event, item)"
>
{{ item.day }}
</td>
</tr>
</template>
</tbody>
</table>
</div>
</template>
<script lang="ts">
export default {
name: "DateContent",
};
</script>
<script lang="ts" setup>
import { useI18n } from "../../../../language";
import { computed, inject } from "vue";
import { provideType } from "../../interface";
export interface DateContentProps {
dateList: any;
modelValue?: number;
startDate?: number;
endDate?: number;
hoverDate?: number;
}
const props = withDefaults(defineProps<DateContentProps>(), {
dateList: [],
modelValue: -1,
hoverDate: -1,
startDate: -1,
endDate: -1,
});
const { t } = useI18n();
const WEEK_NAME = computed(() => [
t("datePicker.sunday"),
t("datePicker.monday"),
t("datePicker.tuesday"),
t("datePicker.wednesday"),
t("datePicker.thursday"),
t("datePicker.friday"),
t("datePicker.saturday"),
]);
const datePicker: provideType = inject("datePicker") as provideType;
const emits = defineEmits([
"update:modelValue",
"update:startDate",
"update:endDate",
"update:hoverDate",
"simple",
]);
// 点击日期
const handleDayClick = (item: any) => {
if (datePicker.range) {
if (item.type !== "current") {
return;
}
if (props.startDate === -1 && props.endDate === -1) {
emits("update:startDate", item.value);
} else if (props.startDate !== -1 && props.endDate !== -1) {
emits("update:hoverDate", item.value);
emits("update:startDate", item.value);
emits("update:endDate", -1);
} else if (props.startDate !== -1 && props.endDate === -1) {
emits("update:endDate", item.value);
if (item.value < props.startDate) {
//swap
const first = props.startDate;
const last = item.value;
emits("update:startDate", last);
emits("update:endDate", first);
}
}
} else {
emits("update:modelValue", item.value);
if (item.type !== "current") {
datePicker.currentMonth.value =
item.type === "prev"
? datePicker.currentMonth.value - 1
: datePicker.currentMonth.value + 1;
}
if (datePicker.simple) {
emits("simple");
}
}
};
const dayItemMouseEnter = (event: MouseEvent, item: any) => {
if (!datePicker.range) {
return;
}
if (props.startDate === -1) {
return;
}
if (item.type !== "current") {
return;
}
if (props.startDate !== -1 && props.endDate !== -1) {
emits("update:hoverDate", -1);
return;
}
emits(
"update:hoverDate",
parseInt((event.target as HTMLElement).dataset.unix as string)
);
};
const ifHasRangeHoverClass = computed(() => {
return function (item: any) {
if (!datePicker.range) {
return false;
}
if (props.startDate === -1) {
return false;
}
if (item.type !== "current") {
return false;
}
if (props.hoverDate === -1 && props.endDate === -1) {
return false;
}
let hover = props.endDate !== -1 ? props.endDate : props.hoverDate;
let max = props.startDate > hover ? props.startDate : hover;
let min = props.startDate < hover ? props.startDate : hover;
if (item.value >= min && item.value <= max) {
return true;
}
return false;
};
});
</script>

View File

@@ -0,0 +1,105 @@
/**
* 获取年份列表
*/
const getYears = () => {
let years = [];
for (let i = 1970; i < getYear() + 100; i++) {
years.push(i);
}
return years;
};
/**
* 获取当前日期
*/
const getDate = (val = "") => {
if (val) {
return new Date(val);
} else {
return new Date();
}
};
/**
* 获取当前年份
*/
const getYear = (val = "") => {
return getDate(val).getFullYear();
};
/**
* 获取当前月份
*/
const getMonth = (val = "") => {
return getDate(val).getMonth();
};
const getDay = (val = "") => {
if (val) {
return new Date(getDate(val).toDateString()).getTime();
} else {
return -1;
}
};
/**
* 获取月份天数
*
* @param year
* @param month
*/
const getDayLength = (year: number, month: number): number => {
return new Date(year, month + 1, 0).getDate();
};
// 设置日期列表
const setDateList = (year: number, month: number) => {
const curDays = getDayLength(year, month); // 当月天数
const prevDays = getDayLength(year, month - 1); // 上月天数
const curFirstDayWeek = new Date(year, month, 1).getDay(); // 当月第一天星期几
const list: any[] = [];
// 填充上月天数
for (let i = prevDays - curFirstDayWeek + 1; i <= prevDays; i++) {
list.push({
day: i,
value: +new Date(year, month - 1, i),
isRange: false,
isSelected: false,
type: "prev",
});
}
// 填充当月天数
for (let i = 1; i <= curDays; i++) {
list.push({
day: i,
value: +new Date(year, month, i),
isRange: false,
isSelected: false,
type: "current",
});
}
// 填充下月天数
const nextDays = 7 - (list.length % 7);
if (nextDays !== 7) {
for (let i = 1; i <= nextDays; i++) {
list.push({
day: i,
value: +new Date(year, month + 1, i),
isRange: false,
isSelected: false,
type: "next",
});
}
}
return list;
};
export {
getDayLength,
getYears,
getDate,
getMonth,
getYear,
getDay,
setDateList,
};

Binary file not shown.

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<!--
2013-9-30: Created.
-->
<svg>
<metadata>
Created by iconfont
</metadata>
<defs>
<font id="laydate-icon" horiz-adv-x="1024" >
<font-face
font-family="laydate-icon"
font-weight="500"
font-stretch="normal"
units-per-em="1024"
ascent="896"
descent="-128"
/>
<missing-glyph />
<glyph glyph-name="x" unicode="x" horiz-adv-x="1001"
d="M281 543q-27 -1 -53 -1h-83q-18 0 -36.5 -6t-32.5 -18.5t-23 -32t-9 -45.5v-76h912v41q0 16 -0.5 30t-0.5 18q0 13 -5 29t-17 29.5t-31.5 22.5t-49.5 9h-133v-97h-438v97zM955 310v-52q0 -23 0.5 -52t0.5 -58t-10.5 -47.5t-26 -30t-33 -16t-31.5 -4.5q-14 -1 -29.5 -0.5
t-29.5 0.5h-32l-45 128h-439l-44 -128h-29h-34q-20 0 -45 1q-25 0 -41 9.5t-25.5 23t-13.5 29.5t-4 30v167h911zM163 247q-12 0 -21 -8.5t-9 -21.5t9 -21.5t21 -8.5q13 0 22 8.5t9 21.5t-9 21.5t-22 8.5zM316 123q-8 -26 -14 -48q-5 -19 -10.5 -37t-7.5 -25t-3 -15t1 -14.5
t9.5 -10.5t21.5 -4h37h67h81h80h64h36q23 0 34 12t2 38q-5 13 -9.5 30.5t-9.5 34.5q-5 19 -11 39h-368zM336 498v228q0 11 2.5 23t10 21.5t20.5 15.5t34 6h188q31 0 51.5 -14.5t20.5 -52.5v-227h-327z" />
<glyph glyph-name="youyou" unicode="&#58882;" d="M283.648 721.918976 340.873216 780.926976 740.352 383.997952 340.876288-12.925952 283.648 46.077952 619.52 383.997952Z" horiz-adv-x="1024" />
<glyph glyph-name="zuozuo" unicode="&#58883;" d="M740.352 721.918976 683.126784 780.926976 283.648 383.997952 683.123712-12.925952 740.352 46.077952 404.48 383.997952Z" horiz-adv-x="1024" />
<glyph glyph-name="xiayiye" unicode="&#58970;" d="M62.573 384.103l423.401 423.662c18.985 18.985 49.757 18.985 68.727 0 18.982-18.972 18.985-49.746 0-68.729l-355.058-355.067 356.796-356.796c18.977-18.971 18.976-49.746 0-68.727-18.982-18.976-49.751-18.976-68.727 0l-39.753 39.753 0.269 0.246-385.655 385.661zM451.365 384.103l423.407 423.662c18.985 18.985 49.757 18.985 68.727 0 18.982-18.972 18.985-49.746 0-68.729l-355.058-355.067 356.796-356.796c18.977-18.971 18.976-49.746 0-68.727-18.982-18.976-49.757-18.977-68.727 0l-39.762 39.754 0.273 0.249-385.662 385.661zM451.365 384.103z" horiz-adv-x="1024" />
<glyph glyph-name="xiayiye1" unicode="&#58971;" d="M948.066926 382.958838l-411.990051-412.24426c-18.47333-18.47333-48.417689-18.47333-66.875207 0-18.47333 18.461167-18.47333 48.405526 0 66.875207L814.691135 383.088983 467.512212 730.269123c-18.466032 18.458735-18.466032 48.405526 0 66.873991 18.468465 18.464816 48.410391 18.464816 66.872774 0l38.682336-38.682336-0.261507-0.239614 375.259894-375.265975v0.003649m-378.312834 0L157.756743-29.285422c-18.47333-18.47333-48.415256-18.47333-66.872775 0-18.47333 18.461167-18.47333 48.405526 0 66.875207L436.369787 383.088983 89.19208 730.269123c-18.4636 18.458735-18.4636 48.405526 0 66.873991 18.470898 18.464816 48.415256 18.464816 66.872774 0l38.692067-38.682336-0.266372-0.239614 375.267191-375.265975-0.004865 0.003649m0 0z" horiz-adv-x="1024" />
</font>
</defs></svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,579 @@
@import "../dropdown/index.less";
@import "../input/index.less";
@lg: 44px;
@md: 38px;
@sm: 32px;
@xs: 26px;
@lg-width: 260px;
@md-width: 220px;
@sm-width: 180px;
@xs-width: 140px;
@lg-range-width: 520px;
@md-range-width: 440px;
@sm-range-width: 360px;
@xs-range-width: 280px;
.set-size(@size,@width) {
& {
width: @width;
height: @size;
.layui-input {
height: @size;
line-height: @size;
}
}
}
.layui-date-picker {
&[size="lg"] {
.set-size(@lg,@lg-width);
}
&[size="md"] {
.set-size(@md,@md-width);
}
&[size="sm"] {
.set-size(@sm,@sm-width);
}
&[size="xs"] {
.set-size(@xs,@xs-width);
}
}
.layui-date-range-picker {
&[size="lg"] {
.set-size(@lg,@lg-range-width);
}
&[size="md"] {
.set-size(@md,@md-range-width);
}
&[size="sm"] {
.set-size(@sm,@sm-range-width);
}
&[size="xs"] {
.set-size(@xs,@xs-range-width);
}
}
@font-face {
font-family: "laydate-icon";
src: url("./font/iconfont.eot");
src: url("./font/iconfont.eot#iefix") format("embedded-opentype"),
url("./font/iconfont.svg#iconfont") format("svg"),
url("./font/iconfont.woff") format("woff"),
url("./font/iconfont.ttf") format("truetype");
}
.laydate-icon {
font-family: "laydate-icon" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
html #layuicss-laydate {
display: none;
position: absolute;
width: 1989px;
}
/* 初始化 */
.layui-laydate * {
margin: 0;
padding: 0;
}
/* 主体结构 */
.layui-laydate,
.layui-laydate * {
box-sizing: border-box;
}
.layui-laydate {
z-index: 66666666;
border-radius: 2px;
font-size: 14px;
-webkit-animation-duration: 0.2s;
animation-duration: 0.2s;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
.layui-laydate-main {
width: 272px;
}
.layui-laydate-header *,
.layui-laydate-content td,
.layui-laydate-list li {
transition-duration: 0.3s;
-webkit-transition-duration: 0.3s;
}
/* 微微往下滑入 */
@keyframes laydate-downbit {
0% {
opacity: 0.3;
transform: translate3d(0, -5px, 0);
}
100% {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
.layui-laydate {
animation-name: laydate-downbit;
}
.layui-laydate-static {
position: relative;
z-index: 0;
display: inline-block;
margin: 0;
-webkit-animation: none;
animation: none;
}
/* 展开年月列表时 */
.laydate-ym-show .laydate-prev-m,
.laydate-ym-show .laydate-next-m {
display: none !important;
}
.laydate-ym-show .laydate-prev-y,
.laydate-ym-show .laydate-next-y {
display: inline-block !important;
}
.laydate-ym-show .laydate-set-ym span[lay-type="month"] {
display: none !important;
}
/* 展开时间列表时 */
.laydate-time-show .layui-laydate-header .layui-icon,
.laydate-time-show .laydate-set-ym span[lay-type="year"],
.laydate-time-show .laydate-set-ym span[lay-type="month"] {
display: none !important;
}
/* 头部结构 */
.layui-laydate-header {
position: relative;
line-height: 30px;
padding: 10px 70px 5px;
}
.layui-laydate-header * {
vertical-align: bottom;
}
.layui-laydate-header i {
position: absolute;
top: 10px;
padding: 0 5px;
color: #999;
font-size: 18px;
cursor: pointer;
}
.layui-laydate-header i.laydate-prev-y {
left: 15px;
}
.layui-laydate-header i.laydate-prev-m {
left: 45px;
}
.layui-laydate-header i.laydate-next-y {
right: 15px;
}
.layui-laydate-header i.laydate-next-m {
right: 45px;
}
.laydate-set-ym {
width: 100%;
text-align: center;
box-sizing: border-box;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.laydate-set-ym span {
padding: 0 10px;
cursor: pointer;
}
.laydate-time-text {
cursor: default !important;
}
/* 主体结构 */
.layui-laydate-content {
position: relative;
padding: 10px;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
}
.layui-laydate-content table {
border-collapse: collapse;
border-spacing: 0;
}
.layui-laydate-content th,
.layui-laydate-content td {
width: 36px;
height: 30px;
padding: 5px;
text-align: center !important;
}
.layui-laydate-content th {
font-weight: 400;
}
.layui-laydate-content td {
position: relative;
cursor: pointer;
}
.laydate-day-mark {
position: absolute;
left: 0;
top: 0;
width: 100%;
line-height: 30px;
font-size: 12px;
overflow: hidden;
}
.laydate-day-mark::after {
position: absolute;
content: "";
right: 2px;
top: 2px;
width: 5px;
height: 5px;
border-radius: 50%;
}
/* 底部结构 */
.layui-laydate-footer {
position: relative;
height: 46px;
line-height: 26px;
padding: 10px;
}
.layui-laydate-footer span {
display: inline-block;
vertical-align: top;
height: 26px;
line-height: 24px;
padding: 0 10px;
border: 1px solid #c9c9c9;
border-radius: 2px;
background-color: #fff;
font-size: 12px;
cursor: pointer;
white-space: nowrap;
transition: all 0.3s;
}
.layui-laydate-footer span:hover {
color: #5fb878;
}
.layui-laydate-footer span.layui-laydate-preview {
cursor: default;
border-color: transparent !important;
}
.layui-laydate-footer span.layui-laydate-preview:hover {
color: #666;
}
.layui-laydate-footer span:first-child.layui-laydate-preview {
padding-left: 0;
}
.laydate-footer-btns {
position: absolute;
right: 10px;
top: 10px;
}
.laydate-footer-btns span {
margin: 0 0 0 -1px;
}
/* 年月列表 */
.layui-laydate-list {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
padding: 10px;
box-sizing: border-box;
background-color: #fff;
}
.layui-laydate-list > li {
position: relative;
display: inline-block;
width: 33.3%;
height: 36px;
line-height: 36px;
margin: 3px 0;
vertical-align: middle;
text-align: center;
cursor: pointer;
}
.laydate-month-list > li {
width: 25%;
margin: 17px 0;
}
.laydate-time-list > li {
height: 100%;
margin: 0;
line-height: normal;
cursor: default;
}
.laydate-time-list p {
position: relative;
top: -4px;
line-height: 29px;
}
.laydate-time-list ol {
height: 181px;
overflow: hidden;
}
.laydate-time-list > li:hover ol {
overflow-y: auto;
}
.laydate-time-list ol li {
width: 130%;
padding-left: 4px;
height: 30px;
line-height: 30px;
text-align: left;
cursor: pointer;
}
/* 提示 */
.layui-laydate-hint {
top: 115px;
left: 50%;
width: 250px;
margin-left: -125px;
line-height: 20px;
padding: 15px;
text-align: center;
font-size: 12px;
color: #ff5722;
}
/* 双日历 */
.layui-laydate-range {
min-width: 546px;
}
.layui-laydate-range .layui-laydate-main {
display: inline-block;
vertical-align: middle;
}
.layui-laydate-range .laydate-main-list-1 .layui-laydate-header,
.layui-laydate-range .laydate-main-list-1 .layui-laydate-content {
border-left: 1px solid #e2e2e2;
}
/* 默认简约主题 */
.layui-laydate,
.layui-laydate-hint {
background-color: #fff;
color: #666;
}
.layui-laydate-header {
border-bottom: 1px solid #e2e2e2;
}
.layui-laydate-header i:hover,
.layui-laydate-header span:hover {
color: #5fb878;
}
.layui-laydate-content {
border-top: none 0;
border-bottom: none 0;
}
.layui-laydate-content th {
color: #333;
}
.layui-laydate-content td {
color: #666;
}
.layui-laydate-content td.laydate-selected {
background-color: #b5fff8;
}
.laydate-selected:hover {
background-color: #00f7de !important;
}
.layui-laydate-content td:hover,
.layui-laydate-list li:hover {
background-color: #eee;
color: #333;
}
.laydate-time-list li ol {
margin: 0;
padding: 0;
border: 1px solid #e2e2e2;
}
.laydate-time-list li:first-child ol {
border-left-width: 1px;
}
.laydate-time-list > li:hover {
background: none;
}
.layui-laydate-content .laydate-day-prev,
.layui-laydate-content .laydate-day-next {
color: #d2d2d2;
}
.laydate-selected.laydate-day-prev,
.laydate-selected.laydate-day-next {
background-color: #f8f8f8 !important;
}
.layui-laydate-footer {
border-top: 1px solid #e2e2e2;
}
.layui-laydate-hint {
color: #ff5722;
}
.laydate-day-mark::after {
background-color: #5fb878;
}
.layui-laydate-content td.layui-this .laydate-day-mark::after {
display: none;
}
.layui-laydate-footer span[lay-type="date"] {
color: #5fb878;
}
.layui-laydate .layui-this {
background-color: #009688 !important;
color: #fff !important;
}
.layui-laydate .laydate-disabled,
.layui-laydate .laydate-disabled:hover {
background: none !important;
color: #d2d2d2 !important;
cursor: not-allowed !important;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
}
/* 墨绿/自定义背景色主题 */
.laydate-theme-molv {
border: none;
}
.laydate-theme-molv.layui-laydate-range {
width: 548px;
}
.laydate-theme-molv .layui-laydate-main {
width: 274px;
}
.laydate-theme-molv .layui-laydate-header {
border: none;
background-color: #009688;
}
.laydate-theme-molv .layui-laydate-header i,
.laydate-theme-molv .layui-laydate-header span {
color: #f6f6f6;
}
.laydate-theme-molv .layui-laydate-header i:hover,
.laydate-theme-molv .layui-laydate-header span:hover {
color: #fff;
}
.laydate-theme-molv .layui-laydate-content {
border: 1px solid #e2e2e2;
border-top: none;
border-bottom: none;
}
.laydate-theme-molv .laydate-main-list-1 .layui-laydate-content {
border-left: none;
}
.laydate-theme-molv .layui-laydate-footer {
border: 1px solid #e2e2e2;
}
/* 格子主题 */
.laydate-theme-grid .layui-laydate-content td,
.laydate-theme-grid .layui-laydate-content thead,
.laydate-theme-grid .laydate-year-list > li,
.laydate-theme-grid .laydate-month-list > li {
border: 1px solid #e2e2e2;
}
.laydate-theme-grid .laydate-selected,
.laydate-theme-grid .laydate-selected:hover {
background-color: #f2f2f2 !important;
color: #009688 !important;
}
.laydate-theme-grid .laydate-selected.laydate-day-prev,
.laydate-theme-grid .laydate-selected.laydate-day-next {
color: #d2d2d2 !important;
}
.laydate-theme-grid .laydate-year-list,
.laydate-theme-grid .laydate-month-list {
margin: 1px 0 0 1px;
}
.laydate-theme-grid .laydate-year-list > li,
.laydate-theme-grid .laydate-month-list > li {
margin: 0 -1px -1px 0;
}
.laydate-theme-grid .laydate-year-list > li {
height: 43px;
line-height: 43px;
}
.laydate-theme-grid .laydate-month-list > li {
height: 71px;
line-height: 71px;
}
.laydate-range-hover{
background-color: var(--global-neutral-color-2) !important;
}
.layui-laydate-content .layui-disabled:hover{
background-color: transparent !important;
}
.laydate-range-inputs{
display: flex;
align-items: center;
border-width: 1px;
border-style: solid;
display: inline-flex;
border-color: var(--input-border-color);
border-radius: var(--input-border-radius);
.range-separator{
margin: 0 5px;
color: var(--global-neutral-color-8);
background-color: transparent;
}
.layui-input-wrapper{
border: none;
box-sizing: border-box;
input{
text-align: center;
padding: 0;
}
}
.layui-input {
border: none;
}
}
.layui-laydate-range{
.laydate-set-ym{
overflow: visible;
white-space: nowrap;
}
.laydate-set-ym .layui-dropdown{
width: auto !important;
}
.time-panel{
.layui-laydate-main{
width: 272px;
display: unset !important;
}
.layui-laydate-preview{
display: none;
}
}
.layui-laydate-content{
.laydate-year-list{
display: flex;
flex-wrap: wrap;
}
}
.layui-laydate-list{
display: flex;
flex-wrap: wrap;
}
}
.layui-laydate-range-datetime{
.layui-laydate-main{
width: 340px;
}
}

View File

@@ -0,0 +1,5 @@
import { withInstall, WithInstallType } from "../../utils";
import Component from "./index.vue";
const component: WithInstallType<typeof Component> = withInstall(Component);
export default component;

View File

@@ -0,0 +1,417 @@
<template>
<div
:class="['layui-date-picker', { 'layui-date-range-picker': range }]"
:size="size"
>
<lay-dropdown
ref="dropdownRef"
:disabled="disabled"
:autoFitMinWidth="false"
:contentClass="contentClass"
:contentStyle="contentStyle"
updateAtScroll
>
<lay-input
:name="name"
:readonly="readonly"
:placeholder="startPlaceholder"
:prefix-icon="prefixIcon"
:suffix-icon="suffixIcon"
:disabled="disabled"
v-model="dateValue"
v-if="!range"
@change="onChange"
@blur="$emit('blur')"
@focus="$emit('focus')"
:allow-clear="!disabled && allowClear"
:size="size"
@clear="
dateValue = '';
onChange();
"
>
</lay-input>
<div class="laydate-range-inputs" v-else>
<lay-input
:readonly="readonly"
:name="name"
v-model="dateValue[0]"
:placeholder="startPlaceholder"
:disabled="disabled"
@change="onChange"
@blur="$emit('blur')"
@focus="$emit('focus')"
class="start-input"
:size="size"
>
</lay-input>
<span class="range-separator">{{ rangeSeparator }}</span>
<lay-input
:readonly="readonly"
:name="name"
:allow-clear="disabled && allowClear"
:placeholder="endPlaceholder"
v-model="dateValue[1]"
:disabled="disabled"
@change="onChange"
@blur="$emit('blur')"
@focus="$emit('focus')"
class="end-input"
:size="size"
@clear="
dateValue = [];
onChange();
"
>
</lay-input>
</div>
<template #content>
<!-- 日期选择 -->
<DatePanel
v-if="!range && (showPanel === 'date' || showPanel === 'datetime')"
v-model="currentDay"
></DatePanel>
<!-- 时间选择 -->
<TimePanel
v-if="!range && showPanel === 'time'"
v-model="hms"
></TimePanel>
<!-- 年份选择器 -->
<YearPanel
v-if="!range && (showPanel === 'year' || showPanel === 'yearmonth')"
v-model="currentYear"
>
</YearPanel>
<!-- 月份选择器 -->
<MonthPanel
v-if="!range && showPanel === 'month'"
v-model="currentMonth"
></MonthPanel>
<!-- 范围选择 -->
<DateRange
v-if="range && (showPanel === 'date' || showPanel === 'datetime')"
v-model:startTime="rangeValue.first"
v-model:endTime="rangeValue.last"
></DateRange>
<MonthRange
v-if="range && showPanel === 'yearmonth'"
v-model:startTime="rangeValue.first"
v-model:endTime="rangeValue.last"
>
</MonthRange>
</template>
</lay-dropdown>
</div>
</template>
<script lang="ts">
export default {
name: "LayDatePicker",
};
</script>
<script lang="ts" setup>
import "./index.less";
import dayjs from "dayjs";
import { LayIcon } from "@layui/icons-vue";
import LayInput from "../input/index.vue";
import LayDropdown from "../dropdown/index.vue";
import { getMonth, getYear, getDay } from "./day";
import {
ref,
watch,
defineProps,
defineEmits,
reactive,
provide,
StyleValue,
} from "vue";
import DatePanel from "./components/DatePanel.vue";
import TimePanel from "./components/TimePanel.vue";
import YearPanel from "./components/YearPanel.vue";
import MonthPanel from "./components/MonthPanel.vue";
import DateRange from "./components/DateRange.vue";
import MonthRange from "./components/MonthRange.vue";
import { computed } from "@vue/reactivity";
export interface DatePickerProps {
type?: "date" | "datetime" | "year" | "time" | "month" | "yearmonth";
placeholder?: string | string[];
modelValue?: string | number | string[];
disabled?: boolean;
simple?: boolean;
name?: string;
max?: string;
min?: string;
range?: boolean;
rangeSeparator?: string;
readonly?: boolean;
allowClear?: boolean;
size?: "lg" | "md" | "sm" | "xs";
prefixIcon?: string;
suffixIcon?: string;
timestamp?: boolean;
contentClass?: string | Array<string | object> | object;
contentStyle?: StyleValue;
}
const props = withDefaults(defineProps<DatePickerProps>(), {
modelValue: "",
type: "date",
disabled: false,
simple: false,
range: false,
rangeSeparator: "至",
readonly: false,
allowClear: false,
size: "md",
prefixIcon: "layui-icon-date",
suffixIcon: "",
timestamp: false,
});
const startPlaceholder = computed(() => {
if (Array.isArray(props.placeholder)) {
return props.placeholder[0];
}
return props.placeholder;
});
const endPlaceholder = computed(() => {
if (Array.isArray(props.placeholder)) {
return props.placeholder[1];
}
return props.placeholder;
});
const dropdownRef = ref(null);
const $emits = defineEmits(["update:modelValue", "change", "blur", "focus"]);
const hms = ref({
hh: 0,
mm: 0,
ss: 0,
});
const currentYear = ref<number>(0);
const currentMonth = ref<number>(0);
const currentDay = ref<number>(0);
const showPanel = ref<string>("date");
const rangeValue = reactive({ first: "", last: "" });
let unWatch = false;
// 计算结果日期
const dateValue = props.range ? ref(["", ""]) : ref("");
const getDateValue = () => {
unWatch = true;
let dayjsVal;
switch (props.type) {
case "date":
dayjsVal =
currentDay.value !== -1
? dayjs(currentDay.value).format("YYYY-MM-DD")
: "";
break;
case "datetime":
dayjsVal =
currentDay.value !== -1
? dayjs(currentDay.value)
.hour(hms.value.hh)
.minute(hms.value.mm)
.second(hms.value.ss)
.format("YYYY-MM-DD HH:mm:ss")
: "";
break;
case "year":
dayjsVal =
currentYear.value !== -1
? dayjs().year(currentYear.value).format("YYYY")
: "";
break;
case "month":
dayjsVal =
currentMonth.value !== -1 ? (currentMonth.value + 1).toString() : "";
break;
case "time":
dayjsVal = dayjs()
.hour(hms.value.hh)
.minute(hms.value.mm)
.second(hms.value.ss)
.format("HH:mm:ss");
break;
case "yearmonth":
dayjsVal =
currentYear.value !== -1 && currentMonth.value !== -1
? dayjs()
.year(currentYear.value)
.month(currentMonth.value)
.format("YYYY-MM")
: "";
break;
default:
dayjsVal =
currentDay.value !== -1
? dayjs(currentDay.value)
.hour(hms.value.hh)
.minute(hms.value.mm)
.second(hms.value.ss)
.format()
: "";
break;
}
dateValue.value = dayjsVal !== "Invalid Date" ? dayjsVal : "";
if (dayjsVal === "Invalid Date") {
unWatch = false;
$emits("update:modelValue", "");
return;
}
if (props.timestamp) {
$emits("update:modelValue", dayjs(dayjsVal).unix() * 1000);
$emits("change", dayjs(dayjsVal).unix() * 1000);
} else {
$emits("update:modelValue", dayjsVal);
$emits("change", dayjsVal);
}
setTimeout(() => {
unWatch = false;
}, 0);
};
const getDateValueByRange = () => {
unWatch = true;
if (rangeValue.first === "" || rangeValue.last === "") {
dateValue.value = ["", ""];
$emits("update:modelValue", dateValue.value);
$emits("change", dateValue.value);
return;
}
let format = "YYYY-MM-DD";
switch (props.type) {
case "date":
format = "YYYY-MM-DD";
break;
case "datetime":
format = "YYYY-MM-DD HH:mm:ss";
break;
case "yearmonth":
format = "YYYY-MM";
break;
}
dateValue.value = [
dayjs(rangeValue.first).format(format),
dayjs(rangeValue.last).format(format),
];
$emits("update:modelValue", dateValue.value);
$emits("change", dateValue.value);
setTimeout(() => {
unWatch = false;
}, 0);
};
// 确认事件
const ok = () => {
if (!props.range) {
getDateValue();
} else {
getDateValueByRange();
}
if (dropdownRef.value)
// @ts-ignore
dropdownRef.value.hide();
showPanel.value = props.type;
};
//面板类型判断
watch(
() => props.type,
() => {
showPanel.value = props.type;
if (props.type === "yearmonth" && !props.range) {
showPanel.value = "year";
}
},
{ immediate: true }
);
//监听modelValue改变
watch(
() => props.modelValue,
() => {
if (unWatch) {
return;
}
let initModelValue: string =
props.range && props.modelValue
? (props.modelValue as string[])[0] || ""
: (props.modelValue as string);
if (props.type === "month" || props.type === "year") {
initModelValue += "";
}
hms.value.hh = isNaN(dayjs(initModelValue).hour())
? 0
: dayjs(initModelValue).hour();
hms.value.mm = isNaN(dayjs(initModelValue).minute())
? 0
: dayjs(initModelValue).minute();
hms.value.ss = isNaN(dayjs(initModelValue).second())
? 0
: dayjs(initModelValue).second();
if (initModelValue.length === 8 && props.type === "time") {
let modelValue = initModelValue;
modelValue = "1970-01-01 " + modelValue;
hms.value.hh = dayjs(modelValue).hour();
hms.value.mm = dayjs(modelValue).minute();
hms.value.ss = dayjs(modelValue).second();
}
currentYear.value = initModelValue ? getYear(initModelValue) : -1;
currentMonth.value = initModelValue ? getMonth(initModelValue) : -1;
currentDay.value = initModelValue ? getDay(initModelValue) : -1;
if (props.type === "date" || props.type === "datetime") {
if (currentYear.value === -1) currentYear.value = dayjs().year();
if (currentMonth.value === -1) currentMonth.value = dayjs().month();
if (props.timestamp) {
currentDay.value = initModelValue
? dayjs(parseInt(initModelValue)).startOf("date").unix() * 1000
: -1;
}
}
rangeValue.first = initModelValue;
rangeValue.last =
props.range && props.modelValue
? (props.modelValue as string[])[1] || ""
: "";
if (!props.range) {
getDateValue();
} else {
getDateValueByRange();
}
},
{ immediate: true }
);
const onChange = () => {
if (dropdownRef.value)
// @ts-ignore
dropdownRef.value.hide();
$emits("update:modelValue", dateValue.value);
};
provide("datePicker", {
currentYear: currentYear,
currentMonth: currentMonth,
currentDay: currentDay,
dateValue: dateValue,
type: props.type,
showPanel: showPanel,
hms: hms,
ok: () => ok(),
getDateValue: () => getDateValue,
range: props.range,
rangeValue: rangeValue,
rangeSeparator: props.rangeSeparator,
simple: props.simple,
timestamp: props.timestamp,
});
</script>

View File

@@ -0,0 +1,27 @@
import { Ref } from "vue";
export type DatePickerType = "date" | "datetime" | "year" | "time" | "month";
export type provideType = {
currentYear: Ref;
currentMonth: Ref;
currentDay: Ref;
dateValue: Ref;
hms: Ref;
type: string;
showPanel: Ref;
clear: Function;
now: Function;
ok: Function;
range: boolean;
rangeValue: {
first: string;
last: string;
hover: string;
firstTime: { hh: number; mm: number; ss: number };
lastTime: { hh: number; mm: number; ss: number };
};
rangeSeparator: string;
simple: boolean;
timestamp: boolean;
};