♻️(component): datePicker 重构

This commit is contained in:
0o张不歪o0 2022-07-14 23:35:55 +08:00
parent 3fc0c38362
commit 188f16a34a
10 changed files with 593 additions and 464 deletions

View File

@ -0,0 +1,160 @@
<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 }} </span>
<span @click="datePicker.showPanel.value = 'month'">{{ datePicker.currentMonth.value + 1 }} </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>
<div class="layui-laydate-content">
<table>
<thead>
<tr>
<th v-for="item of WEEK_NAME" :key="item">{{ item }}</th>
</tr>
</thead>
<tbody>
<template v-for="(o, i) of datePicker.dateList.value.length % 7 == 0
? datePicker.dateList.value.length / 7
: Math.floor(datePicker.dateList.value.length / 7) + 1" :key="i">
<tr>
<td v-for="(item, index) of datePicker.dateList.value.slice(
i * 7,
i * 7 + 7
)" :key="index" :data-unix="item.value" :class="{
'laydate-day-prev': item.type !== 'current',
'layui-this': item.value === datePicker.currentDay.value || (range && (item.value == parseInt(rangeValue.first) || item.value == parseInt(rangeValue.last))),
'laydate-range-hover': ifHasRangeHoverClass(item),
}" @click="handleDayClick(item)" @mouseenter="dayItemMouseEnter($event, item)">
{{ item.day }}
</td>
</tr>
</template>
</tbody>
</table>
</div>
<PanelFoot></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'
export interface Props {
range?: boolean,
rangeValue?: { first: string, last: string, hover: string }
}
const props = withDefaults(defineProps<Props>(), {
range: false,
rangeValue: () => { return { first: '', last: '', hover: '' } }
});
const datePicker: provideType = inject('datePicker') as provideType;
const WEEK_NAME = ["日", "一", "二", "三", "四", "五", "六"];
// ,
watch(
[datePicker.currentYear, datePicker.currentMonth],
() => {
datePicker.dateList.value = setDateList(datePicker.currentYear.value, datePicker.currentMonth.value);
},
{ immediate: true }
);
//
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 handleDayClick = (item: any) => {
if (props.range) {
if (item.type !== "current") {
return;
}
}
datePicker.currentDay.value = item.value;
if (item.type !== "current") {
datePicker.currentMonth.value = item.type === "prev" ? datePicker.currentMonth.value - 1 : datePicker.currentMonth.value + 1;
}
if (props.range) {
if (props.rangeValue.first === '' && props.rangeValue.last === '') {
//props.rangeValue.first = item.value
} else if (props.rangeValue.first !== '' && props.rangeValue.last !== '') {
// rangeValue.first = item.value
// rangeValue.last = ''
} else if (props.rangeValue.first !== '' && props.rangeValue.last === '') {
//rangeValue.last = item.value
}
}
};
const dayItemMouseEnter = (event: MouseEvent, item: any) => {
if (!props.range) {
return;
}
if (!props.rangeValue.first) {
return;
}
if (item.type !== 'current') {
return;
}
//rpropsangeValue.hover = (event.target as HTMLElement).dataset.unix as string
}
const ifHasRangeHoverClass = computed(() => {
return function (item: any) {
if (!props.range) {
return false
}
if (!props.rangeValue.first) {
return false
}
if (item.type !== 'current') {
return false
}
if (item.value == parseInt(props.rangeValue.first) || item.value == parseInt(props.rangeValue.last)) {
return true;
}
let hover = props.rangeValue.last ? props.rangeValue.last : props.rangeValue.hover;
let max = props.rangeValue.first > hover ? props.rangeValue.first : hover;
let min = props.rangeValue.first < hover ? props.rangeValue.first : hover
if (item.value >= parseInt(min) && item.value <= parseInt(max)) {
return true
}
return false
}
})
</script>

View File

@ -0,0 +1,85 @@
<template>
<div class="layui-laydate">
<div class="layui-laydate-main laydate-main-list-0 laydate-ym-show">
<div class="layui-laydate-header">
<i class="layui-icon laydate-icon laydate-prev-y" @click="changeYearOrMonth('year', -1)"></i>
<div class="laydate-set-ym">
<span @click="datePicker.showPanel.value = 'month'">{{ datePicker.currentMonth.value + 1 }} </span>
</div>
<i class="layui-icon laydate-icon laydate-next-y" @click="changeYearOrMonth('year', 1)"></i>
</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) === datePicker.currentMonth.value }"
@click="handleMonthClick(item)">
{{ item.slice(0, 3) }}
</li>
</ul>
</div>
<PanelFoot></PanelFoot>
</div>
</template>
<script lang="ts">
export default {
name: "TimePanel",
};
</script>
<script lang="ts" setup>
import dayjs from 'dayjs';
import { inject } from 'vue';
import { provideType } from '../interface';
import PanelFoot from './PanelFoot.vue'
const datePicker: provideType = inject('datePicker') as provideType;
const MONTH_NAME = [
"1月",
"2月",
"3月",
"4月",
"5月",
"6月",
"7月",
"8月",
"9月",
"10月",
"11月",
"12月",
];
//
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 handleMonthClick = (item: any) => {
datePicker.currentMonth.value = MONTH_NAME.indexOf(item);
if (datePicker.type === "month") {
datePicker.currentDay.value = dayjs(datePicker.currentDay.value)
.month(MONTH_NAME.indexOf(item))
.valueOf();
} else if (datePicker.type === "yearmonth") {
datePicker.currentDay.value = dayjs(datePicker.currentDay.value)
.month(MONTH_NAME.indexOf(item))
.valueOf();
} else {
datePicker.showPanel.value = "date";
}
};
</script>

View File

@ -0,0 +1,47 @@
<template>
<div class="layui-laydate-footer">
<span v-if="datePicker.type === 'datetime' && datePicker.showPanel.value === 'datetime'" @click="chooseTime"
class="laydate-btns-time">选择时间</span>
<span v-else-if="datePicker.type === 'datetime' && datePicker.showPanel.value === 'time'"
@click="datePicker.showPanel.value = 'datetime'" class="laydate-btns-time">选择日期</span>
<span v-else-if="datePicker.type === 'yearmonth' && datePicker.showPanel.value === 'year'"
@click="datePicker.showPanel.value = 'month'" class="laydate-btns-time">选择月份</span>
<span v-else-if="datePicker.type === 'yearmonth' && datePicker.showPanel.value === 'month'"
@click="datePicker.showPanel.value = 'year'" class="laydate-btns-time">选择年份</span>
<span v-else-if="!datePicker.range" class="layui-laydate-preview" title="当前选中的结果">
<template v-if="datePicker.type === 'month'">{{ datePicker.currentMonth.value + 1 }}</template>
<template v-else-if="datePicker.type === 'year'">{{ datePicker.currentYear.value }}</template>
<template v-else-if="datePicker.type === 'time'">{{
datePicker.hms.value.hh + ':' + datePicker.hms.value.mm + ':' + (datePicker.hms.value.ss === 0 ? '0' : '') + datePicker.hms.value.ss
}}</template>
</span>
<div class="laydate-footer-btns">
<span lay-type="clear" class="laydate-btns-clear" @click="datePicker.clear()">清空</span>
<span lay-type="now" class="laydate-btns-now" @click="datePicker.now()">现在</span>
<span lay-type="confirm" class="laydate-btns-confirm" @click="datePicker.ok()">确定</span>
</div>
</div>
</template>
<script lang="ts">
export default {
name: "PanelFoot",
};
</script>
<script lang="ts" setup>
import { inject } from 'vue';
import { provideType } from '../interface';
const datePicker: provideType = inject('datePicker') as provideType;
const emits = defineEmits(['showYearPanel', 'update:showPanel', 'scrollToCurrentTime', 'ok']);
//
const chooseTime = () => {
datePicker.showPanel.value = 'time'
//scrollToCurrentTime()
}
</script>

View File

@ -0,0 +1,53 @@
<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">选择时间</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="choseTime">
<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.toString().padStart(2, '0') == datePicker.hms.value[item.type]
? 'layui-this'
: '',
]">
{{ index.toString().padStart(2, "0") }}
</li>
</ol>
</li>
</ul>
</div>
</div>
<PanelFoot></PanelFoot>
</div>
</template>
<script lang="ts">
export default {
name: "TimePanel",
};
</script>
<script lang="ts" setup>
import { inject } from 'vue';
import { provideType } from '../interface';
import PanelFoot from './PanelFoot.vue'
const datePicker: provideType = inject('datePicker') as provideType;
const els = [
{ count: 24, type: "hh" },
{ count: 60, type: "mm" },
{ count: 60, type: "ss" },
];
// - hms
const choseTime = (e: any) => {
if (e.target.nodeName == "LI") {
let { value, type } = e.target.dataset;
datePicker.hms.value[type as keyof typeof datePicker.hms.value] = value;
}
};
</script>

View File

@ -0,0 +1,43 @@
<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">选择年份</span>
</div>
</div>
</div>
<div class="layui-laydate-content" style="height: 220px; overflow-y: auto" ref="yearmonthScrollRef">
<ul class="layui-laydate-list laydate-year-list">
<li v-for="item of datePicker.yearList.value" :key="item"
:class="{ 'layui-this': datePicker.currentYear.value === item }" @click="handleYearClick(item)">{{ item }}</li>
</ul>
</div>
<PanelFoot></PanelFoot>
</div>
</template>
<script lang="ts">
export default {
name: "YearPanel",
};
</script>
<script lang="ts" setup>
import dayjs from 'dayjs';
import { inject } from 'vue';
import { provideType } from '../interface';
import PanelFoot from './PanelFoot.vue'
const datePicker: provideType = inject('datePicker') as provideType;
//
const handleYearClick = (item: any) => {
datePicker.currentYear.value = item;
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";
} else {
datePicker.showPanel.value = "date";
}
};
</script>

View File

@ -52,4 +52,47 @@ const getDayLength = (year: number, month: number): number => {
return new Date(year, month + 1, 0).getDate(); return new Date(year, month + 1, 0).getDate();
}; };
export { getDayLength, getYears, getDate, getMonth, getYear, getDay };
// 设置日期列表
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 };

View File

@ -459,3 +459,10 @@ html #layuicss-laydate {
height: 71px; height: 71px;
line-height: 71px; line-height: 71px;
} }
.laydate-range-hover{
background-color: var(--global-checked-color) !important;
color: white !important;
}
.layui-laydate-content .layui-disabled:hover{
background-color: transparent !important;
}

View File

@ -1,277 +1,19 @@
<template> <template>
<div> <div>
<lay-dropdown ref="dropdownRef" :disabled="disabled" @open="openDropDown"> <lay-dropdown ref="dropdownRef" :disabled="disabled" @open="openDropDown">
<lay-input <lay-input readonly :name="name" :model-value="dateValue" :placeholder="placeholder" prefix-icon="layui-icon-date"
readonly :disabled="disabled">
:name="name"
:model-value="dateValue || modelValue"
:placeholder="placeholder"
prefix-icon="layui-icon-date"
:disabled="disabled"
>
</lay-input> </lay-input>
<template #content> <template #content>
<!-- 日期选择 --> <!-- 日期选择 -->
<div <DatePanel v-if="!range && (showPanel === 'date' || showPanel === 'datetime')">
class="layui-laydate" </DatePanel>
v-show="showPane === 'date' || showPane === 'datetime'" <!-- 时间选择 -->
> <TimePanel v-if="!range && showPanel === 'time'"></TimePanel>
<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="showYearPanel">{{ currentYear }} </span>
<span @click="showPane = 'month'"
>{{ currentMonth + 1 }} </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>
<div class="layui-laydate-content">
<table>
<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 === currentDay,
}"
@click="handleDayClick(item)"
>
{{ item.day }}
</td>
</tr>
</template>
</tbody>
</table>
</div>
</div>
<div class="layui-laydate-footer">
<span
v-if="type === 'datetime'"
@click="
showPane = 'time';
scrollToCurrentTime();
"
class="laydate-btns-time"
>选择时间</span
>
<div class="laydate-footer-btns">
<span lay-type="clear" class="laydate-btns-clear" @click="clear"
>清空</span
>
<span lay-type="now" class="laydate-btns-now" @click="now"
>现在</span
>
<span lay-type="confirm" class="laydate-btns-confirm" @click="ok"
>确定</span
>
</div>
</div>
</div>
<!-- 年份选择器 --> <!-- 年份选择器 -->
<div <YearPanel v-if="!range && (showPanel === 'year' || showPanel === 'yearmonth')"></YearPanel>
class="layui-laydate"
v-show="showPane === 'year' || showPane === 'yearmonth'"
>
<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">选择年份</span>
</div>
</div>
<div
class="layui-laydate-content"
style="height: 220px; overflow-y: auto"
ref="yearmonthScrollRef"
>
<ul class="layui-laydate-list laydate-year-list">
<li
v-for="item of yearList"
:key="item"
:class="[{ 'layui-this': currentYear === item }]"
@click="handleYearClick(item)"
>
{{ item }}
</li>
</ul>
</div>
</div>
<div class="layui-laydate-footer">
<span
class="layui-laydate-preview"
title="当前选中的结果"
style="color: rgb(102, 102, 102)"
>{{ dateValue }}</span
>
<div class="laydate-footer-btns">
<span lay-type="clear" class="laydate-btns-clear" @click="clear"
>清空</span
>
<span lay-type="now" class="laydate-btns-now" @click="now"
>现在</span
>
<span lay-type="confirm" class="laydate-btns-confirm" @click="ok"
>确定</span
>
</div>
</div>
</div>
<!-- 月份选择器 --> <!-- 月份选择器 -->
<div class="layui-laydate" v-show="showPane === 'month'"> <MonthPanel v-if="!range && showPanel === 'month'"></MonthPanel>
<div class="layui-laydate-main laydate-main-list-0 laydate-ym-show">
<div class="layui-laydate-header">
<i
class="layui-icon laydate-icon laydate-prev-y"
@click="changeYearOrMonth('year', -1)"
></i
>
<div class="laydate-set-ym">
<span
@click="showYearPanel"
v-if="showPane === 'date' || showPane === 'datetime'"
>{{ currentYear }} </span
>
<span @click="showPane = 'month'"
>{{ currentMonth + 1 }} </span
>
</div>
<i
class="layui-icon laydate-icon laydate-next-y"
@click="changeYearOrMonth('year', 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"
:class="[
{ 'layui-this': MONTH_NAME.indexOf(item) === currentMonth },
]"
@click="handleMonthClick(item)"
>
{{ item.slice(0, 3) }}
</li>
</ul>
</div>
</div>
<div class="layui-laydate-footer">
<span
class="layui-laydate-preview"
title="当前选中的结果"
style="color: rgb(102, 102, 102)"
>{{ dateValue }}</span
>
<div class="laydate-footer-btns">
<span lay-type="clear" class="laydate-btns-clear" @click="clear"
>清空</span
>
<span lay-type="now" class="laydate-btns-now" @click="now"
>现在</span
>
<span lay-type="confirm" class="laydate-btns-confirm" @click="ok"
>确定</span
>
</div>
</div>
</div>
<!-- 时间选择器 -->
<div class="layui-laydate" v-if="showPane == 'time'">
<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">选择时间</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="choseTime">
<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.toString().padStart(2, '0') == hms[item.type]
? 'layui-this'
: '',
]"
>
{{ index.toString().padStart(2, "0") }}
</li>
</ol>
</li>
</ul>
</div>
</div>
<div class="layui-laydate-footer">
<span
@click="showPane = 'date'"
v-if="type != 'time'"
class="laydate-btns-time"
>返回日期</span
>
<div class="laydate-footer-btns">
<span lay-type="clear" class="laydate-btns-clear" @click="clear"
>清空</span
>
<span lay-type="now" class="laydate-btns-now" @click="now"
>现在</span
>
<span lay-type="confirm" class="laydate-btns-confirm" @click="ok"
>确定</span
>
</div>
</div>
</div>
</template> </template>
</lay-dropdown> </lay-dropdown>
</div> </div>
@ -290,16 +32,11 @@ import { LayIcon } from "@layui/icons-vue";
import LayInput from "../input/index.vue"; import LayInput from "../input/index.vue";
import LayDropdown from "../dropdown/index.vue"; import LayDropdown from "../dropdown/index.vue";
import { getDayLength, getYears, getMonth, getYear, getDay } from "./day"; import { getDayLength, getYears, getMonth, getYear, getDay } from "./day";
import { import { ref, watch, computed, defineProps, defineEmits, onMounted, nextTick, reactive, provide } from "vue";
ref, import DatePanel from './components/DatePanel.vue';
watch, import TimePanel from './components/TimePanel.vue';
computed, import YearPanel from './components/YearPanel.vue';
defineProps, import MonthPanel from './components/MonthPanel.vue';
defineEmits,
onMounted,
nextTick,
} from "vue";
import { anyTypeAnnotation } from "@babel/types";
export interface LayDatePickerProps { export interface LayDatePickerProps {
type?: "date" | "datetime" | "year" | "time" | "month" | "yearmonth"; type?: "date" | "datetime" | "year" | "time" | "month" | "yearmonth";
@ -310,6 +47,7 @@ export interface LayDatePickerProps {
name?: string; name?: string;
max?: string; max?: string;
min?: string; min?: string;
range?: boolean
} }
const props = withDefaults(defineProps<LayDatePickerProps>(), { const props = withDefaults(defineProps<LayDatePickerProps>(), {
@ -317,26 +55,13 @@ const props = withDefaults(defineProps<LayDatePickerProps>(), {
type: "date", type: "date",
disabled: false, disabled: false,
simple: false, simple: false,
range: false
}); });
const dropdownRef = ref(null); const dropdownRef = ref(null);
const $emits = defineEmits(["update:modelValue"]); const $emits = defineEmits(["update:modelValue"]);
const WEEK_NAME = ["日", "一", "二", "三", "四", "五", "六"];
const MONTH_NAME = [
"1月",
"2月",
"3月",
"4月",
"5月",
"6月",
"7月",
"8月",
"9月",
"10月",
"11月",
"12月",
];
const hms = ref({ const hms = ref({
hh: dayjs(props.modelValue).hour(), hh: dayjs(props.modelValue).hour(),
@ -344,31 +69,30 @@ const hms = ref({
ss: dayjs(props.modelValue).second(), ss: dayjs(props.modelValue).second(),
}); });
const els = [
{ count: 24, type: "hh" },
{ count: 60, type: "mm" },
{ count: 60, type: "ss" },
];
const currentYear = ref(getYear(props.modelValue)); const currentYear = ref(getYear(props.modelValue));
const currentMonth = ref(getMonth(props.modelValue)); const currentMonth = ref(getMonth(props.modelValue));
const currentDay = ref<number>(getDay(props.modelValue)); const currentDay = ref<number>(getDay(props.modelValue));
const yearList = ref<number[]>(getYears()); const yearList = ref<number[]>(getYears());
const dateList = ref<any[]>([]); const dateList = ref<any[]>([]);
const showPane = ref("date"); const dateListNext = ref<any[]>([]);
const showPanel = ref("date");
const yearmonthScrollRef = ref(); const yearmonthScrollRef = ref()
watch( watch(
() => props.type, () => props.type,
() => { () => {
showPane.value = props.type; showPanel.value = props.type;
if (props.type === 'yearmonth') {
showPanel.value = 'year';
}
}, },
{ immediate: true } { immediate: true }
); );
onMounted(() => { onMounted(() => {
//
if (currentDay.value == -1) { if (currentDay.value == -1) {
setTimeout(() => { setTimeout(() => {
now(); now();
@ -376,21 +100,29 @@ onMounted(() => {
}, 0); }, 0);
} }
let modelValue = props.modelValue; let modelValue = props.modelValue;
if (modelValue.length === 8) { if (modelValue.length === 8) { //dayjs
//dayjs modelValue = '1970-01-01 ' + modelValue;
modelValue = "1970-01-01 " + modelValue;
} }
hms.value.hh = dayjs(modelValue).hour(); hms.value.hh = dayjs(modelValue).hour();
hms.value.mm = dayjs(modelValue).minute(); hms.value.mm = dayjs(modelValue).minute();
hms.value.ss = dayjs(modelValue).second(); hms.value.ss = dayjs(modelValue).second();
getDateValue()
}); });
// //
const dateValue = computed<string>(() => { const dateValue = ref('')
if (currentDay.value === -1) { const getDateValue = (checkCurrentDay = true) => {
$emits("update:modelValue", ""); if (checkCurrentDay) {
return ""; if (currentDay.value === -1) {
$emits("update:modelValue", "");
return "";
}
} else {
if (currentDay.value === -1) {
currentDay.value = new Date(new Date().toDateString()).getTime();
}
} }
let dayjsVal; let dayjsVal;
let dayjsObj = dayjs(currentDay.value) let dayjsObj = dayjs(currentDay.value)
.hour(hms.value.hh) .hour(hms.value.hh)
@ -419,77 +151,14 @@ const dateValue = computed<string>(() => {
dayjsVal = dayjsObj.format(); dayjsVal = dayjsObj.format();
break; break;
} }
dateValue.value = dayjsVal;
$emits("update:modelValue", dayjsVal); $emits("update:modelValue", dayjsVal);
if (props.simple && unWatch.value) {
ok();
}
setTimeout(() => {
unWatch.value = false;
}, 0);
return dayjsVal; return dayjsVal;
});
//
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",
});
}
}
dateList.value = list;
}; };
// ,
watch(
[currentYear, currentMonth],
() => {
setDateList(currentYear.value, currentMonth.value);
},
{ immediate: true }
);
const unWatch = ref(true);
watch(
() => props.modelValue,
() => {
if (!unWatch.value) {
currentDay.value = new Date(props.modelValue).getTime();
}
}
);
// //
const ok = () => { const ok = () => {
getDateValue(false)
if (dropdownRef.value) if (dropdownRef.value)
// @ts-ignore // @ts-ignore
dropdownRef.value.hide(); dropdownRef.value.hide();
@ -497,7 +166,6 @@ const ok = () => {
// //
const now = () => { const now = () => {
unWatch.value = true;
currentDay.value = dayjs().valueOf(); currentDay.value = dayjs().valueOf();
hms.value.hh = dayjs().hour(); hms.value.hh = dayjs().hour();
hms.value.mm = dayjs().minute(); hms.value.mm = dayjs().minute();
@ -506,104 +174,65 @@ const now = () => {
// //
const clear = () => { const clear = () => {
unWatch.value = true;
currentDay.value = -1; currentDay.value = -1;
}; };
// //simple
const changeYearOrMonth = (type: "year" | "month", num: number) => { watch(currentDay, () => {
if (type === "year") { if (props.simple) {
currentYear.value += num; getDateValue();
} else { ok();
let month = currentMonth.value + num;
if (month > 11) {
month = 0;
currentYear.value++;
} else if (month < 0) {
month = 11;
currentYear.value--;
}
currentMonth.value = month;
} }
}; })
watch(currentMonth, () => {
// if (props.simple && props.type !== 'date') {
const showYearPanel = () => { getDateValue();
showPane.value = "year"; ok();
};
//
const handleYearClick = (item: any) => {
unWatch.value = true;
currentYear.value = item;
if (props.type === "year") {
currentDay.value = dayjs().year(item).valueOf();
} else if (props.type === "yearmonth") {
currentDay.value = dayjs().year(item).valueOf();
showPane.value = "month";
} else {
showPane.value = "date";
} }
}; })
watch(currentYear, () => {
// if (props.simple && props.type !== 'date') {
const handleMonthClick = (item: any) => { getDateValue();
unWatch.value = true; ok();
currentMonth.value = MONTH_NAME.indexOf(item);
if (props.type === "month") {
currentDay.value = dayjs(currentDay.value)
.month(MONTH_NAME.indexOf(item))
.valueOf();
} else if (props.type === "yearmonth") {
currentDay.value = dayjs(currentDay.value)
.month(MONTH_NAME.indexOf(item))
.valueOf();
} else {
showPane.value = "date";
} }
}; })
// provide('datePicker', {
const handleDayClick = (item: any) => { currentYear: currentYear,
unWatch.value = true; currentMonth: currentMonth,
currentDay.value = item.value; currentDay: currentDay,
if (item.type !== "current") { dateValue: dateValue,
currentMonth.value = type: props.type,
item.type === "prev" ? currentMonth.value - 1 : currentMonth.value + 1; showPanel: showPanel,
} dateList: dateList,
}; yearList: yearList,
hms: hms,
// - hms clear: () => clear(),
const choseTime = (e: any) => { now: () => now(),
unWatch.value = true; ok: () => ok(),
if (e.target.nodeName == "LI") { getDateValue: () => getDateValue,
let { value, type } = e.target.dataset; range: props.range
hms.value[type as keyof typeof hms.value] = value; });
}
};
const openDropDown = () => { const openDropDown = () => {
nextTick(() => { nextTick(() => {
if (showPane.value == "year" || showPane.value == "yearmonth") { // if (showPanel.value == 'year' || showPanel.value == 'yearmonth') {
let scrollTop = 0; // let scrollTop = 0;
for (const child of yearmonthScrollRef.value.firstElementChild // for (const child of yearmonthScrollRef.value.firstElementChild.childNodes) {
.childNodes) { // if (child.classList && child.classList.contains('layui-this')) {
if (child.classList && child.classList.contains("layui-this")) { // scrollTop = child.offsetTop - (yearmonthScrollRef.value.offsetHeight - child.offsetHeight) / 2;
scrollTop = // break;
child.offsetTop - // }
(yearmonthScrollRef.value.offsetHeight - child.offsetHeight) / 2; // }
break; // yearmonthScrollRef.value.scrollTo(0, scrollTop)
} // } else if (showPanel.value == 'time') {
} // scrollToCurrentTime()
yearmonthScrollRef.value.scrollTo(0, scrollTop); // }
} else if (showPane.value == "time") { })
scrollToCurrentTime(); }
}
});
};
// //
const timePanelRef = ref(); const timePanelRef = ref()
const scrollToCurrentTime = () => { const scrollToCurrentTime = () => {
nextTick(() => { nextTick(() => {
timePanelRef.value.childNodes.forEach((element: HTMLElement) => { timePanelRef.value.childNodes.forEach((element: HTMLElement) => {
@ -626,6 +255,46 @@ const scrollToCurrentTime = () => {
} }
} }
}); });
}); })
}; }
//
const rangeValue = reactive({
first: '', last: '', hover: ''
})
const dayItemMouseEnter = (event: MouseEvent, item: any) => {
if (!props.range) {
return;
}
if (!rangeValue.first) {
return;
}
if (item.type !== 'current') {
return;
}
rangeValue.hover = (event.target as HTMLElement).dataset.unix as string
}
const ifHasRangeHoverClass = computed(() => {
return function (item: any) {
if (!props.range) {
return false
}
if (!rangeValue.first) {
return false
}
if (item.type !== 'current') {
return false
}
if (item.value == parseInt(rangeValue.first) || item.value == parseInt(rangeValue.last)) {
return true;
}
let hover = rangeValue.last ? rangeValue.last : rangeValue.hover;
let max = rangeValue.first > hover ? rangeValue.first : hover;
let min = rangeValue.first < hover ? rangeValue.first : hover
if (item.value >= parseInt(min) && item.value <= parseInt(max)) {
return true
}
return false
}
})
</script> </script>

View File

@ -1 +1,19 @@
import { Ref } from "vue";
export type DatePickerType = "date" | "datetime" | "year" | "time" | "month"; export type DatePickerType = "date" | "datetime" | "year" | "time" | "month";
export type provideType={
currentYear:Ref,
currentMonth:Ref,
currentDay:Ref,
dateValue:Ref,
dateList:Ref,
yearList:Ref,
hms:Ref,
type:string,
showPanel:Ref,
clear:Function,
now:Function,
ok:Function,
range:boolean,
}

View File

@ -193,10 +193,14 @@ export default {
::: title 一次性选择 ::: title 一次性选择
::: :::
::: demo 只需要点击一次后自动关闭,无需点击确认按钮 ::: demo 只需要点击一次后自动关闭,无需点击确认按钮,仅在type等于`year`、`month`、`date`时有效
<template> <template>
<lay-date-picker v-model="endTime7" simple></lay-date-picker> <div style="display:flex">
<lay-date-picker v-model="endTime7" simple type="year"></lay-date-picker>
<lay-date-picker v-model="endTime7" simple type="month" style="margin:0 10px"></lay-date-picker>
<lay-date-picker v-model="endTime7" simple type="date"></lay-date-picker>
</div>
</template> </template>
<script> <script>