time-select.vue 6.07 KB
<template>
  <view class="time-select">
    <view class="tab-container">
      <view class="tab-item" :class="{ active: currentDateType === 'week' }" @click="switchDateType('week')">

      </view>
      <view class="tab-item" :class="{ active: currentDateType === 'month' }" @click="switchDateType('month')">

      </view>
    </view>

    <view class="date-stepper">
      <view class="arrow-icon" @click="handlePrev">
        <up-icon name="arrow-left" color="#333" size="12" bold></up-icon>
      </view>
      <view class="date-display"> {{ dateRangeText }} </view>
      <!-- 修改: 根据 isNextDisabled 判断是否隐藏下一个按钮 -->
      <view class="arrow-icon" @click="handleNext" v-if="!isNextDisabled">
        <up-icon name="arrow-right" color="#333" size="12" bold></up-icon>
      </view>
      <view v-else class="occupy"></view>
    </view>
  </view>
</template>

<script setup>
import { ref, computed, watch } from 'vue';

const emit = defineEmits(['dateTypeChange', 'dateRangeChange']);

// 当前日期类型: 'week' | 'month'
const currentDateType = ref('week');
// 基准日期,用于计算范围
const baseDate = ref(new Date());

// 格式化日期辅助函数 YYYY/MM/DD
const formatDate = (date) => {
  const y = date.getFullYear();
  const m = String(date.getMonth() + 1).padStart(2, '0');
  const d = String(date.getDate()).padStart(2, '0');
  return `${y}/${m}/${d}`;
};
// 格式化日期辅助函数 YYYY-MM-DD
const formatDateYmd = (date) => {
  const y = date.getFullYear();
  const m = String(date.getMonth() + 1).padStart(2, '0');
  const d = String(date.getDate()).padStart(2, '0');
  return `${y}-${m}-${d}`;
};

const currentDateRange = computed(() => {
  const current = new Date(baseDate.value);
  let start, end;

  if (currentDateType.value === 'week') {
    const day = current.getDay();
    const diffToMonday = day === 0 ? -6 : 1 - day;
    start = new Date(current);
    start.setDate(current.getDate() + diffToMonday);
    end = new Date(start);
    end.setDate(start.getDate() + 6);
  } else {
    start = new Date(current.getFullYear(), current.getMonth(), 1);
    end = new Date(current.getFullYear(), current.getMonth() + 1, 0);
  }

  return {
    start: formatDateYmd(start),
    end: formatDateYmd(end)
  };
});

// 计算显示的日期范围文本
const dateRangeText = computed(() => {
  const current = new Date(baseDate.value);

  if (currentDateType.value === 'week') {
    // 计算本周的周一和周日
    const day = current.getDay();
    const diffToMonday = day === 0 ? -6 : 1 - day; // 调整到周一

    const startOfWeek = new Date(current);
    startOfWeek.setDate(current.getDate() + diffToMonday);

    const endOfWeek = new Date(startOfWeek);
    endOfWeek.setDate(startOfWeek.getDate() + 6);

    return `${formatDate(startOfWeek)} - ${formatDate(endOfWeek)}`;
  } else {
    // 计算本月的一号和最后一天
    const startOfMonth = new Date(current.getFullYear(), current.getMonth(), 1);
    const endOfMonth = new Date(current.getFullYear(), current.getMonth() + 1, 0);

    return `${formatDate(startOfMonth)} - ${formatDate(endOfMonth)}`;
  }
});

// 新增: 计算下一个日期是否不可用(即是否为未来日期)
const isNextDisabled = computed(() => {
  const newDate = new Date(baseDate.value);
  if (currentDateType.value === 'week') {
    newDate.setDate(newDate.getDate() + 7);
  } else {
    newDate.setMonth(newDate.getMonth() + 1);
  }

  // 重置时分秒进行比较
  const now = new Date();
  now.setHours(0, 0, 0, 0);
  const checkDate = new Date(newDate);
  checkDate.setHours(0, 0, 0, 0);

  return checkDate > now;
});

// 切换周/月
const switchDateType = (type) => {
  currentDateType.value = type;
  emit('dateTypeChange', type);
};

// 上一段时间
const handlePrev = () => {
  const newDate = new Date(baseDate.value);
  if (currentDateType.value === 'week') {
    newDate.setDate(newDate.getDate() - 7);
  } else {
    newDate.setMonth(newDate.getMonth() - 1);
  }
  baseDate.value = newDate;
};

// 下一段时间
const handleNext = () => {
  // 虽然按钮已隐藏,但保留逻辑判断以防直接调用或其他边界情况
  if (isNextDisabled.value) {
    return;
  }

  const newDate = new Date(baseDate.value);
  if (currentDateType.value === 'week') {
    newDate.setDate(newDate.getDate() + 7);
  } else {
    newDate.setMonth(newDate.getMonth() + 1);
  }

  baseDate.value = newDate;
};

watch(
  [baseDate, currentDateType],
  () => {
    emit('dateRangeChange', {
      startDate: currentDateRange.value.start,
      endDate: currentDateRange.value.end
    });
  },
  { immediate: true }
);
</script>

<style scoped lang="scss">
.time-select {
  flex-shrink: 0;
  /* 修改: 防止顶部选择器被压缩 */
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;

  // 周月切换 Tab
  .tab-container {
    width: 100%;
    height: 72rpx;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8rpx;
    background-color: #eee;
    border-radius: 12rpx;
    padding: 6rpx;
    box-sizing: border-box;

    // #ifdef H5
    margin-top: 30px;
    // #endif

    .tab-item {
      display: flex;
      align-items: center;
      justify-content: center;
      color: #8c8c8c;
      font-size: 28rpx;
      transition: all 0.2s ease;

      &.active {
        background-color: #ffffff;
        color: #1a1a1a;
        font-weight: 600;
        border-radius: 8rpx;
        box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
      }
    }
  }

  // 日期翻页器
  .date-stepper {
    margin-top: 32rpx;
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;

    .date-display {
      margin: 0 40rpx;
      font-size: 28rpx;
      font-weight: 500;
      color: #333;
      font-variant-numeric: tabular-nums; // 防止数字切换时抖动
    }

    .arrow-icon {
      width: 56rpx;
      height: 56rpx;
      display: flex;
      align-items: center;
      justify-content: center;
      background-color: #fff;
      border-radius: 50%;
      transition: background-color 0.2s;
    }
  }

  .occupy {
    width: 56rpx;
    height: 56rpx;
  }
}
</style>