jiuyiUniapp/jiuyi2/TUIKit/components/common/DatePicker/date-picker-panel.vue

309 lines
7.9 KiB
Vue

<template>
<div
:class="[n('')]"
@mouseup.stop
>
<div :class="[n('body')]">
<div :class="[n('body-header')]">
<div :class="[n('body-header-prev')]">
<div
v-if="canYearLess"
:class="[n('icon')]"
@click="change('year', -1)"
>
<Icon
:file="dLeftArrowIcon"
:width="'12px'"
:height="'12px'"
/>
</div>
<div
v-if="canMonthLess"
:class="[n('icon')]"
@click="change('month', -1)"
>
<Icon
:file="leftArrowIcon"
:width="'10px'"
:height="'10px'"
/>
</div>
</div>
<div :class="[n('body-header-label')]">
<div :class="[n('body-header-label-item')]">
{{ year }}
</div>
<div :class="[n('body-header-label-item')]">
{{ TUITranslateService.t(`time.${month}`) }}
</div>
</div>
<div :class="[n('body-header-next')]">
<div
v-if="canMonthMore"
:class="[n('icon')]"
@click="change('month', 1)"
>
<Icon
:file="rightArrowIcon"
:width="'10px'"
:height="'10px'"
/>
</div>
<div
v-if="canYearMore"
:class="[n('icon')]"
@click="change('year', 1)"
>
<Icon
:file="dRightArrowIcon"
:width="'12px'"
:height="'12px'"
/>
</div>
</div>
</div>
<div :class="[n('body-content')]">
<DateTable
:type="props.type"
:date="props.date"
:startDate="props.startDate"
:endDate="props.endDate"
:currentPanelDate="currentPanelDate"
@pick="handlePick"
/>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed, ref, onBeforeMount } from '../../../adapter-vue';
import dayjs, { Dayjs, ManipulateType } from 'dayjs';
import { TUITranslateService } from '@tencentcloud/chat-uikit-engine';
import { DateCell } from './date-picker';
import DateTable from './date-table.vue';
import Icon from '../Icon.vue';
import leftArrowIcon from '../../../assets/icon/left-arrow.svg';
import rightArrowIcon from '../../../assets/icon/right-arrow.svg';
import dLeftArrowIcon from '../../../assets/icon/d-left-arrow.svg';
import dRightArrowIcon from '../../../assets/icon/d-right-arrow.svg';
import { isPC } from '../../../utils/env';
const props = defineProps({
type: {
type: String,
default: 'range', // "single"/"range"
},
// Unique attribute when type is single
date: {
type: Dayjs,
default: () => dayjs(),
},
// Unique attribute when type is range
startDate: {
type: Dayjs,
default: null,
},
endDate: {
type: Dayjs,
default: null,
},
rangeType: {
type: String,
default: '', // "left"/"right"
},
currentOtherPanelValue: {
type: Dayjs,
default: null,
},
});
const emit = defineEmits(['pick', 'change']);
const n = (className: string) => {
return className
? [
'tui-date-picker-panel-' + className,
!isPC && 'tui-date-picker-panel-h5-' + className,
]
: ['tui-date-picker-panel', !isPC && 'tui-date-picker-panel-h5'];
};
const currentPanelDate = ref<typeof Dayjs>();
const year = computed(() => currentPanelDate.value?.get('year'));
const month = computed(() => currentPanelDate.value?.format('MMMM'));
const canYearMore = computed(() => {
const prevYearNumber = props.currentOtherPanelValue?.year() - 1;
const prevYear = props.currentOtherPanelValue?.year(prevYearNumber);
return (
props.rangeType === 'right'
|| currentPanelDate.value?.isBefore(prevYear, 'year')
);
});
const canMonthMore = computed(() => {
const prevMonthNumber = props.currentOtherPanelValue?.month() - 1;
const prevMonth = props.currentOtherPanelValue?.month(prevMonthNumber);
return (
props.rangeType === 'right'
|| currentPanelDate.value?.isBefore(prevMonth, 'month')
);
});
const canYearLess = computed(() => {
const nextYearNumber = props.currentOtherPanelValue?.year() + 1;
const nextYear = props.currentOtherPanelValue?.year(nextYearNumber);
return (
props.rangeType === 'left'
|| currentPanelDate.value?.isAfter(nextYear, 'year')
);
});
const canMonthLess = computed(() => {
const nextMonthNumber = props.currentOtherPanelValue?.month() + 1;
const nextMonth = props.currentOtherPanelValue?.month(nextMonthNumber);
return (
props.rangeType === 'left'
|| currentPanelDate.value?.isAfter(nextMonth, 'month')
);
});
// Range judgment:
// Premise: If there is only one, it must be the start.
// If there is a startDate:
// When the left side of the interface first displays the month/year of the startDate.
// If there is both a startDate and an endDate:
// If they are in the same month:
// Both are displayed on the left, and the next month is displayed on the right.
// If they are not in the same month:
// The start is displayed on the left, and the end is displayed on the right.
// That is, to determine whether the start and end are in the same month.
// If neither is present, the left displays the current month, and the right displays the next month.
const handleSingleDate = (): { date: typeof Dayjs } => {
if (props.date && dayjs(props.date)?.isValid()) {
// props.date year and month
return {
date: props?.date,
};
}
// nowadays year and month
return {
date: dayjs(),
};
};
const handleRangeDate = (): { date: typeof Dayjs } => {
switch (props.rangeType) {
case 'left':
if (props.startDate && dayjs.isDayjs(props.startDate)) {
return {
date: props?.startDate,
};
} else {
return {
date: dayjs(),
};
}
case 'right':
if (
props.endDate
&& dayjs.isDayjs(props.endDate)
&& props?.endDate?.isAfter(props.startDate, 'month')
) {
return {
date: props?.endDate,
};
} else {
const _month = (props.startDate || dayjs()).month();
return {
date: (props.startDate || dayjs()).month(_month + 1),
};
}
default:
return {
date: dayjs(),
};
}
};
function handlePick(cell: DateCell) {
emit('pick', cell);
}
function change(type: typeof ManipulateType, num: number) {
currentPanelDate.value = dayjs(currentPanelDate.value.toDate()).add(
num,
type,
);
emit('change', currentPanelDate.value);
}
onBeforeMount(() => {
switch (props.type) {
case 'single':
currentPanelDate.value = handleSingleDate().date;
emit('change', currentPanelDate.value);
break;
case 'range':
currentPanelDate.value = handleRangeDate().date;
emit('change', currentPanelDate.value);
break;
}
});
</script>
<style scoped lang="scss">
.tui-date-picker-panel {
width: 200px;
margin: 5px;
&-body {
width: 200px;
display: flex;
flex-direction: column;
&-header {
width: 100%;
display: flex;
flex-direction: row;
height: 30px;
padding: 0 5px;
box-sizing: border-box;
&-prev {
display: flex;
flex-direction: row;
cursor: pointer;
width: 24px;
}
&-label {
flex: 1;
display: flex;
flex-direction: row;
text-align: center;
align-items: center;
justify-content: center;
user-select: none;
color: #666;
&-item {
padding: 0 5px;
color: #666;
}
}
&-next {
display: flex;
flex-direction: row;
cursor: pointer;
width: 24px;
}
}
}
&-icon {
display: flex;
justify-content: center;
align-items: center;
width: 12px;
}
}
</style>