123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322 |
- <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>
|