消息
This commit is contained in:
321
TUIKit/components/common/DatePicker/date-table.vue
Normal file
321
TUIKit/components/common/DatePicker/date-table.vue
Normal file
@@ -0,0 +1,321 @@
|
||||
<template>
|
||||
<table
|
||||
:class="['tui-date-table', !isPC && 'tui-date-table-h5']"
|
||||
cellspacing="0"
|
||||
cellpadding="0"
|
||||
role="grid"
|
||||
>
|
||||
<tbody class="tui-date-table-body">
|
||||
<tr class="tui-date-table-body-weeks">
|
||||
<th
|
||||
v-for="item in WEEKS"
|
||||
:key="item"
|
||||
class="tui-date-table-body-weeks-item"
|
||||
:aria-label="item + ''"
|
||||
scope="col"
|
||||
>
|
||||
{{ TUITranslateService.t(`time.${item}`) }}
|
||||
</th>
|
||||
</tr>
|
||||
<tr
|
||||
v-for="(row, rowKey) in rows"
|
||||
:key="rowKey"
|
||||
class="tui-date-table-body-days"
|
||||
>
|
||||
<td
|
||||
v-for="(col, colKey) in row"
|
||||
:key="colKey"
|
||||
:class="['tui-date-table-body-days-item', col.type]"
|
||||
>
|
||||
<div
|
||||
:class="[
|
||||
'tui-date-table-body-days-item-cell',
|
||||
col.isSelected && 'selected',
|
||||
col.isSelectedStart && 'selected-start',
|
||||
col.isSelectedEnd && 'selected-end',
|
||||
col.isInRange && 'range',
|
||||
]"
|
||||
@click="handlePick(col)"
|
||||
>
|
||||
<span class="tui-date-table-body-days-item-cell-text">
|
||||
{{ col.text }}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
computed,
|
||||
ref,
|
||||
getCurrentInstance,
|
||||
nextTick,
|
||||
watch,
|
||||
} from '../../../adapter-vue';
|
||||
import { TUITranslateService } from '@tencentcloud/chat-uikit-engine';
|
||||
import dayjs, { Dayjs } from 'dayjs';
|
||||
import 'dayjs/locale/zh-cn';
|
||||
import { DateCell, DateCellType } from './date-picker';
|
||||
import { isPC } from '../../../utils/env';
|
||||
|
||||
const props = defineProps({
|
||||
type: {
|
||||
type: String,
|
||||
default: 'range', // "single"/"range"
|
||||
},
|
||||
currentPanelDate: {
|
||||
type: Dayjs,
|
||||
default: () => dayjs(),
|
||||
},
|
||||
// Unique attribute when type is single
|
||||
date: {
|
||||
type: Dayjs,
|
||||
default: null,
|
||||
},
|
||||
// Unique attribute when type is range
|
||||
startDate: {
|
||||
type: Dayjs,
|
||||
default: null,
|
||||
},
|
||||
endDate: {
|
||||
type: Dayjs,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['pick']);
|
||||
// vue instance
|
||||
const instance = getCurrentInstance();
|
||||
|
||||
const tableRows = ref<DateCell[][]>([[], [], [], [], [], []]);
|
||||
const currentPanelDateObject = ref<typeof Dayjs>(
|
||||
dayjs(props.currentPanelDate || null),
|
||||
);
|
||||
const dateObject = ref<typeof Dayjs>(dayjs(props.date || null));
|
||||
const startDateObject = ref<typeof Dayjs>(dayjs(props.startDate || null));
|
||||
const endDateObject = ref<typeof Dayjs>(dayjs(props.endDate || null));
|
||||
|
||||
const WEEKS_CONSTANT = computed(() => {
|
||||
return dayjs.weekdaysShort();
|
||||
});
|
||||
|
||||
const WEEKS = computed(() =>
|
||||
WEEKS_CONSTANT.value.map((w: string) => w.substring(1)),
|
||||
);
|
||||
|
||||
const startDateOnTable = computed(() => {
|
||||
const startDayOfMonth = currentPanelDateObject.value?.startOf('month');
|
||||
return startDayOfMonth?.subtract(startDayOfMonth?.day() || 7, 'day');
|
||||
});
|
||||
|
||||
// Table data
|
||||
const rows = computed(() => {
|
||||
const rows_ = tableRows.value;
|
||||
const cols = WEEKS.value.length;
|
||||
|
||||
const startOfMonth = currentPanelDateObject.value?.startOf('month');
|
||||
const startOfMonthDay = startOfMonth?.day() || 7; // day of this month first day
|
||||
const dateCountOfMonth = startOfMonth?.daysInMonth(); // total days of this month
|
||||
|
||||
let count = 1;
|
||||
for (let row = 0; row < tableRows.value.length; row++) {
|
||||
for (let col = 0; col < cols; col++) {
|
||||
const cellDate = startDateOnTable.value?.add(count, 'day');
|
||||
const text = cellDate?.date();
|
||||
// For type === "single", select the entered date
|
||||
// For type === "range", select the entered start and end dates
|
||||
const isSelected
|
||||
= props.type === 'single'
|
||||
&& cellDate?.format('YYYY-MM-DD')
|
||||
=== dateObject.value?.format('YYYY-MM-DD');
|
||||
const isSelectedStart
|
||||
= props.type === 'range'
|
||||
&& cellDate?.format('YYYY-MM-DD')
|
||||
=== startDateObject.value?.format('YYYY-MM-DD');
|
||||
const isSelectedEnd
|
||||
= props.type === 'range'
|
||||
&& cellDate?.format('YYYY-MM-DD')
|
||||
=== endDateObject.value?.format('YYYY-MM-DD');
|
||||
// For type === "range", check if it is within the selected range.
|
||||
const isInRange
|
||||
= cellDate?.isSameOrBefore(endDateObject.value, 'day')
|
||||
&& cellDate?.isSameOrAfter(startDateObject.value, 'day');
|
||||
let type: DateCellType = 'normal';
|
||||
if (count < startOfMonthDay) {
|
||||
// Prev month's date
|
||||
type = 'prev-month';
|
||||
} else if (count - startOfMonthDay >= dateCountOfMonth) {
|
||||
// Next month's date
|
||||
type = 'next-month';
|
||||
}
|
||||
rows_[row][col] = {
|
||||
type,
|
||||
date: cellDate,
|
||||
text,
|
||||
isSelected: isSelected || isSelectedStart || isSelectedEnd,
|
||||
isSelectedStart,
|
||||
isSelectedEnd,
|
||||
isInRange,
|
||||
};
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return rows_;
|
||||
});
|
||||
|
||||
const handlePick = (cell: DateCell) => {
|
||||
if (cell?.type !== 'normal') {
|
||||
return;
|
||||
}
|
||||
emit('pick', cell);
|
||||
};
|
||||
|
||||
watch(
|
||||
() => [props.currentPanelDate, props.date, props.startDate, props.endDate],
|
||||
() => {
|
||||
currentPanelDateObject.value = dayjs(props.currentPanelDate || null);
|
||||
dateObject.value = dayjs(props.date || null);
|
||||
startDateObject.value = dayjs(props.startDate || null);
|
||||
endDateObject.value = dayjs(props.endDate || null);
|
||||
nextTick(() => {
|
||||
instance?.proxy?.$forceUpdate();
|
||||
});
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
/* stylelint-disable selector-class-pattern */
|
||||
.tui-date-table {
|
||||
border-spacing: 0;
|
||||
-webkit-border-horizontal-spacing: 0;
|
||||
-webkit-border-vertical-spacing: 0;
|
||||
font-size: 12px;
|
||||
user-select: none;
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
&::after,
|
||||
&::before {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
&-body {
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
|
||||
&-weeks,
|
||||
&-days {
|
||||
box-sizing: border-box;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&-weeks {
|
||||
width: 100%;
|
||||
|
||||
&-item {
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
font-weight: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
&-days {
|
||||
color: #000;
|
||||
|
||||
&-item {
|
||||
&-cell {
|
||||
text-align: center;
|
||||
padding: 2px;
|
||||
margin: 2px 0;
|
||||
|
||||
&-text {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
.selected {
|
||||
border-radius: 12px;
|
||||
|
||||
.tui-date-table-body-days-item-cell-text {
|
||||
box-sizing: border-box;
|
||||
color: #007aff;
|
||||
border: 1px solid #007aff;
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.range {
|
||||
background-color: #007aff33;
|
||||
}
|
||||
|
||||
.selected-start {
|
||||
border-radius: 12px 0 0 12px;
|
||||
}
|
||||
|
||||
.selected-end {
|
||||
border-radius: 0 12px 12px 0;
|
||||
}
|
||||
|
||||
.selected-start.selected-end {
|
||||
border-radius: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.prev-month,
|
||||
.next-month {
|
||||
color: #666;
|
||||
background-color: #fff;
|
||||
|
||||
.range {
|
||||
color: #666;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.selected {
|
||||
.tui-date-table-body-days-item-cell-text {
|
||||
box-sizing: border-box;
|
||||
color: #666;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tui-date-table-h5 {
|
||||
|
||||
/* stylelint-disable-next-line no-descending-specificity */
|
||||
.tui-date-table-body-days-item-cell-text {
|
||||
cursor: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
td,
|
||||
._td,
|
||||
.tui-date-table-body-days-item {
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user