日历选择YYYY-MM 单选

74 阅读2分钟

结构

image.png

popupBox_Content_calendar_YYYYMM_radio.vue

<template>
  <view class="box">
    <view style="height: calc(100%); overflow-y: hidden">
      <scroll-view
        scroll-y
        style="height: 100%"
        :scroll-top="scrollTop"
        lower-threshold="200"
        @scrolltolower="scrolltolower"
        refresher-enabled
        :refresher-triggered="refresherTriggered"
        refresher-background="transparent"
        @refresherrefresh="pullDownRefresh"
      >
        <template v-for="item in YearArr">
          <view class="item">
            <view class="year">
              <view>{{ item.year }}</view>
            </view>

            <view class="months">
              <template v-for="month in item.months">
                <!-- <view
              :class="[
                'monthBox',
                `Select_first_${selectdate.includes(month)}`,
                { Select_disable_true: compare(dateMax, month) == 1 },
              ]"
              @click="select(month)"
            >
              <view class="value">{{ dayFormat(month) }}</view>
            </view> -->
                <view
                  :class="[
                    'monthBox',
                    `Select_first_${selectdate == month ? true : false}`,
                  ]"
                  :id="`item_${month}`"
                  @click="select(month)"
                  v-if="
                    dateMax == 'infinite'
                      ? true
                      : [0, -1].includes(compare(dateMax, month))
                  "
                >
                  <view class="value">{{ dayFormat(month) }}</view>
                </view>
              </template>
            </view>
          </view>
        </template>
      </scroll-view>
    </view>
  </view>
</template>

<script setup>
import { ref, onMounted, computed } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app";
const props = defineProps({
  date: {
    type: String,
    default:
      new Date().getFullYear() +
      "-" +
      (new Date().getMonth() + 1 + "").padStart(2, "0"),
  },
  dateMax: {
    type: String,
    default: () => {
      const currentDate = new Date();
      const year = currentDate.getFullYear();
      const month = (currentDate.getMonth() + 1 + "").padStart(2, "0");
      return `${year}-${month}`;
    },
  },
});
const YearArr = ref([]);
const dateMax = ref(props.dateMax);
const selectdate = ref(props.date);
onLoad(() => {
  let arr = [];
  let year;
  year = new Date(selectdate.value).getFullYear();
  for (let index = 0; index < 3; index++) {
    arr.unshift({
      year,
      months: [
        year + "-01",
        year + "-02",
        year + "-03",
        year + "-04",
        year + "-05",
        year + "-06",
        year + "-07",
        year + "-08",
        year + "-09",
        year + "-10",
        year + "-11",
        year + "-12",
      ],
    });
    year -= 1;
  }
  YearArr.value = arr;
  setTimeout(() => {
    scrollToElement(selectdate.value);
  }, 500);
});

// 选择日期
function select(date) {
  selectdate.value = `${date}`;
}
//日期格式化 - 月的
function dayFormat(date) {
  let day = new Date(date).getMonth() + 1 + "月";
  return day;
}
//比较日期大小YYYY-MM的
// 相同返回0
// 大于返回1
// 小于返回-1
function compare(date1, date2) {
  let newDate1 = date1.replace(/-/g, "");
  let newDate2 = date2.replace(/-/g, "");
  if (newDate1 == newDate2) {
    return 0;
  }
  if (newDate1 < newDate2) {
    return 1;
  }
  if (newDate1 > newDate2) {
    return -1;
  }
}

//下拉加载上一年数据
const refresherTriggered = ref(false);
function pullDownRefresh() {
  refresherTriggered.value = true;
  let year = YearArr.value[0].year - 1;
  setTimeout(() => {
    refresherTriggered.value = false;
    YearArr.value.unshift({
      year,
      months: [
        year + "-01",
        year + "-02",
        year + "-03",
        year + "-04",
        year + "-05",
        year + "-06",
        year + "-07",
        year + "-08",
        year + "-09",
        year + "-10",
        year + "-11",
        year + "-12",
      ],
    });
  }, 500);
}
// 到底加载下一年数据
function scrolltolower() {
  if (
    dateMax.value !== "infinite" &&
    compare(YearArr.value[YearArr.value.length - 1].months[11],dateMax.value) !== 1
  ) {
    return;
  }
  let year = YearArr.value[YearArr.value.length - 1].year + 1;
  YearArr.value.push({
    year,
    months: [
      year + "-01",
      year + "-02",
      year + "-03",
      year + "-04",
      year + "-05",
      year + "-06",
      year + "-07",
      year + "-08",
      year + "-09",
      year + "-10",
      year + "-11",
      year + "-12",
    ],
  });
}
//跳转到指定的年月
const scrollTop = ref(0);
function scrollToElement(domid) {
  console.log("domid", "#item_" + domid);
  /*#ifdef APP-PLUS*/
  uni
    .createSelectorQuery()
    .select("#item_" + domid)
    .boundingClientRect((rect) => {
      if (rect) {
        scrollTop.value = rect.top;
      }
    })
    .exec();
  /*#endif*/
  /*#ifdef H5*/
  document.querySelector("#item_" + domid).scrollIntoView(true);
  /*#endif*/
}
defineExpose({
  selectdate,
  dateMax,
});
</script>

<style lang="scss" scoped>
.box {
  // transform: scaleY(-1);
  flex: 1;
  position: relative;
  overflow-y: hidden;
  // padding-right: 20rpx;
}
.item {
  // transform: scaleY(-1);

  .year {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 50px;
    font-weight: 600;
  }

  .months {
    display: flex;
    justify-content: left;
    flex-wrap: wrap;
    .monthBox {
      height: 100rpx;
      width: 25%;
      display: flex;
      align-items: center;
      justify-content: center;
      // background-color:red;
    }
    .Select_true {
      color: yellow;
    }
    .Select_disable_true {
      position: relative;
      &:before {
        content: "";
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        height: 80rpx;
        width: 80rpx;
        border-radius: 50%;
        background-color: #b5b5b5;
      }
      .value {
        color: #fff;
        z-index: 9;
      }
    }
    .Select_first_true {
      position: relative;
      &:before {
        content: "";
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        height: 80rpx;
        width: 130rpx;
        border-radius: 10rpx;
        background-color: #337cfa;
      }
      .value {
        color: #fff;
        z-index: 9;
      }
    }
    .Select_last_true {
      color: blue;
    }
  }
}
</style>

popupBox_calendar_YYYYMM_radio.vue,使用弹窗容器包裹,完成最后的组件

<template>
  <popupBox ref="YYYYMM_Popup" :onConfirmDisable="onConfirmDisable">
    <template #title>
      <view class="top">
        <text class="title">选择月份</text>
        <text class="remark"
          >已选:{{ popupBox_Content_calendar_YYYYMM_Ref?.selectdate }}</text
        >
      </view>
    </template>
    <template #content>
      <popupBox_Content_calendar_YYYYMM
        ref="popupBox_Content_calendar_YYYYMM_Ref"
        :date="date"
      ></popupBox_Content_calendar_YYYYMM>
    </template>
  </popupBox>
</template>
<script setup>
import popupBox_Content_calendar_YYYYMM from "./popupBox_Content_calendar_YYYYMM_radio.vue";
import { ref, computed, onMounted, watch, defineEmits, nextTick } from "vue";
const popupBox_Content_calendar_YYYYMM_Ref = ref(null);
const YYYYMM_Popup = ref(null);
const emit = defineEmits(["update:modelValue", "onConfirm"]);
const onConfirmDisable = computed(() => {
  if (popupBox_Content_calendar_YYYYMM_Ref.value?.selectdate[0]) {
    return false;
  } else {
    return true;
  }
});
const date = ref("");
function open(params) {
  if (params.date) {
    date.value = params.date;
  } else {
    date.value =
      new Date().getFullYear() +
      "-" +
      (new Date().getMonth() + 1 + "").padStart(2, "0");
  }
  nextTick(() => {
    //打开弹窗
    YYYYMM_Popup.value.open({
      cancelText: "取消",
      confirmText: "确认",
      onOpen: function () {
        if (params.dateMax) {
          popupBox_Content_calendar_YYYYMM_Ref.value.dateMax = params.dateMax;
        }
      },
      onConfirm: function () {
        if (params.onConfirm) {
          params.onConfirm(
            popupBox_Content_calendar_YYYYMM_Ref.value.selectdate
          );
        }
        emit(
          "onConfirm",
          popupBox_Content_calendar_YYYYMM_Ref.value.selectdate
        );
        YYYYMM_Popup.value.cancel();
      },
    });
  });
}
defineExpose({
  open,
});
</script>
<style scoped lang="scss">
:deep {
  .popBox {
    .content {
      // transform: scaleY(-1);
      display: flex;
    }
  }
}
.top {
  .title {
    font-size: 34.35rpx;
    font-weight: 600;
  }
  .remark {
    margin-left: 30rpx;
    color: rgba(122, 122, 122, 1);
    font-size: 26.71rpx;
  }
}
</style>

使用及效果

<template>
  <view @click="open">选月 (单选)</view>
  <!-- 日历月单选 -->
  <popupBox_calendar_YYYYMM_radio ref="YYYYMM_Popup_radio"></popupBox_calendar_YYYYMM_radio>
</template>

<script setup>
import { ref, onMounted, computed } from "vue";
//月单选
const date = ref("2023-12")
const YYYYMM_Popup_radio = ref(null);
function open() {
	YYYYMM_Popup_radio.value.open({
		date: date3.value,
		dateMax: "2024-01",//传入infinite代表无限制,可以一直往上滚加载,如果传入YYYY-MM-DD为格式的日期则可选最大日期就是这个,且无法上滚加载,默认值为当月
		onConfirm: (value) => {
			date3.value = value;
		},
	});
}
</script>