日历YYYY-MM-DD选择 单选/多选 组件

63 阅读8分钟

uniapp提供的日期日历组件不满足需求,且控制麻烦,这个组件可以通过上下滑动加载日期,且打开时会将滚动条跳到选中的日期中,如以后想在组件内加其他功能也可以通过修改组件实现

单选

结构

image.png

popupBox_Content_calendar_YYYYMMDD_radio.vue

<template>
  <view class="box">
    <view class="week">
      <view
        v-for="item in ['一', '二', '三', '四', '五', '六', '日']"
        class="item"
        >{{ item }}</view
      >
    </view>
    <view style="height: calc(100% - 80rpx); 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"
      >
        <!-- <scroll-view
        scroll-y
        style="height: 100%"
        :scroll-top="scrollTop"
        upper-threshold="0"
        lower-threshold="200"
        @scrolltolower="scrolltolower"
        @scrolltoupper="scrolltoupper"
      > -->
        <view v-for="months in dateArr">
          <view
            class="month"
            v-if="
              dateShow2(`${months.year}-${(months.month + '').padStart(2, 0)}`)
            "
            :id="`item_${months.year}-${(months.month + '').padStart(2, 0)}`"
            >{{ `${months.year}年-${months.month}月` }}</view
          >
          <view v-for="days in months.days" class="lineBox">
            <view
              v-for="day in days"
              :class="['item', `Select_first_${isSelect(day, months.month)}`]"
              @click="getday(day, months.month)"
              v-if="dateShow(days[0])"
            >
              <view class="value" v-if="dateShow(day)">{{
                dayFormat(day, months.month)
              }}</view>
            </view>
          </view>
        </view>
      </scroll-view>
    </view>
  </view>
</template>

<script setup>
import { ref, onMounted, computed, nextTick } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app";
const props = defineProps({
  date: {
    type: String,
    default:() => {
      const currentDate = new Date();
      const year = currentDate.getFullYear();
      const month = (currentDate.getMonth() + 1 + "").padStart(2, "0");
      const day = currentDate.getDate().toString().padStart(2, "0");
      return `${year}-${month}-${day}`;
    },
    validator: (value) => {
      if (!value || value == "" || !/^\d{4}-\d{2}-\d{2}$/.test(value)) {
        return false; // 日期格式不正确
      }
      return true; // 日期格式正确
    },
  },
  dateMax: {
    type: String,
    default:
      new Date().getFullYear() +
      "-" +
      (new Date().getMonth() + 1 + "").padStart(2, "0") +
      "-" +
      new Date().getDate().toString().padStart(2, "0"),
    validator: (value) => {
      if (value == "infinite") {
        return true;
      }
      if (!value || !/^\d{4}-\d{2}-\d{2}$/.test(value)) {
        return false; // 日期格式不正确
      }
      return true; // 日期格式正确
    },
  },
});
//传入年 获取整年的日历数组
function getdateArr(year) {
  function getdays(year, month) {
    function getDatesBetween(rangeStart, rangeEnd) {
      let dates = [];
      let currentDate = new Date(rangeStart);
      while (currentDate <= rangeEnd) {
        dates.push(currentDate.toISOString().slice(0, 10));

        currentDate.setDate(currentDate.getDate() + 1);
      }
      return dates;
    }
    function getDaysOfMonth(year, month) {
      let startDay = new Date(year, month - 1, 1);
      let endDay = new Date(year, month, 0);
      // 找到第一个星期日
      while (startDay.getDay() !== 2) {
        startDay.setDate(startDay.getDate() - 1);
      }
      // 找到最后一个星期六
      while (endDay.getDay() !== 1) {
        endDay.setDate(endDay.getDate() + 1);
      }
      return getDatesBetween(startDay, endDay);
    }
    let daysOfMonth = getDaysOfMonth(year, month);
    //分割为7个一组
    function groupDates(dates) {
      let groupedArray = [];
      let groupCount = Math.ceil(dates.length / 7);
      for (let i = 0; i < groupCount; i++) {
        let startIndex = i * 7;
        let endIndex = (i + 1) * 7;
        let group = dates.slice(startIndex, endIndex);
        groupedArray.push(group);
      }
      return groupedArray;
    }
    let group = groupDates(daysOfMonth);
    return group;
  }
  const months = [];
  for (let month = 0; month < 12; month++) {
    const monthValue = month + 1;
    months.push({
      year: year,
      month: monthValue,
      days: getdays(year, monthValue),
    });
  }
  return months;
}
const dateArr = ref([]);
onLoad(() => {
  let year = Number(props.date.slice(0, 4));
  dateArr.value = getdateArr(year - 1).concat(getdateArr(year));
});
//到顶加载上一年数据
function scrolltoupper() {
  const year = dateArr.value[0].year - 1;
  let newdate = getdateArr(year);
  setTimeout(() => {
    scrollToElement(`${year + 1}-12`);
    dateArr.value = newdate.concat(dateArr.value);
  }, 500);
}
//下拉加载上一年数据
const refresherTriggered = ref(false);
function pullDownRefresh() {
  refresherTriggered.value = true;
  const year = dateArr.value[0].year - 1;
  let newdate = getdateArr(year);
  setTimeout(() => {
    scrollToElement(`${year + 1}-12`);
    refresherTriggered.value = false;
    dateArr.value = newdate.concat(dateArr.value);
  }, 500);
}
// 到底加载下一年数据
function scrolltolower() {
  if (
    props.dateMax !== "infinite" &&
    dateArr.value[dateArr.value.length - 1].year == props.dateMax.slice(0, 4)
  ) {
    return;
  }
  const year = dateArr.value[dateArr.value.length - 1].year + 1;
  let newdate = getdateArr(year);
  dateArr.value = dateArr.value.concat(newdate);
}
//日期格式化
function dayFormat(date, month) {
  if (new Date(date).getMonth() + 1 !== month) {
    return "";
  }
  let day = new Date(date).getDate();
  return day;
}
//日期显示与否
function dateShow(day) {
  if (
    props.dateMax !== "infinite" &&
    Number(props.dateMax.replace(/-/g, "")) < Number(day.replace(/-/g, ""))
  ) {
    return false;
  }
  return true;
}
function dateShow2(day) {
  if (
    props.dateMax !== "infinite" &&
    Number(props.dateMax.substring(0, 8).replace(/-/g, "")) <
      Number(day.replace(/-/g, ""))
  ) {
    return false;
  }
  return true;
}
//选择该项
const selectdate = ref(props.date);
function getday(date, month) {
  const isInSameMonth = dayFormat(date, month);
  if (!isInSameMonth) {
    return;
  }
  const isSmallerThanDateMax = dateShow(date);
  if (!isSmallerThanDateMax) {
    return;
  }
  selectdate.value = date;
}
//判断该项是否选中
function isSelect(date, month) {
  if (new Date(date).getMonth() + 1 !== month) {
    return false;
  }
  if (selectdate.value == date) {
    return true;
  }
}
//跳转到指定的年月
const scrollTop = ref(0);
function scrollToElement(domid) {
  /*#ifdef APP-PLUS*/
  uni
    .createSelectorQuery()
    .select("#item_" + domid)
    .boundingClientRect((rect) => {
      if (rect) {
        scrollTop.value = rect.top - 250;
      }
    })
    .exec();
	/*#endif*/
  /*#ifdef H5*/
  document.querySelector("#item_" + domid).scrollIntoView(true);
  /*#endif*/
}

onLoad(() => {
  setTimeout(() => {
    scrollToElement(props.date.slice(0, 7));
  }, 500);
});
defineExpose({
  selectdate,
});
</script>

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

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

.lineBox {
  display: flex;
  justify-content: space-between;
  align-items: center;

  .item {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 80rpx;
    height: 80rpx;
  }

  .Select_first_true {
    position: relative;

    &:before {
      content: "";
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      height: 80rpx;
      width: 80rpx;
      border-radius: 50%;
      background-color: #337cfa;
    }

    .value {
      color: #fff;
      z-index: 9;
    }
  }
}

.years {
  .item {
    font-size: 23rpx;
    background-color: #337cfa;
    color: #fff;
    display: flex;
    justify-content: center;
    align-items: center;
    border-radius: 50%;
    padding: 5rpx;
    margin-top: 20rpx;
  }
}
.week {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-bottom: 30rpx;
  border-bottom: 1px solid rgba(217, 217, 217, 1);
  .item {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 80rpx;
  }
}
</style>

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

<template>
  <popupBox ref="YYYYMM_Popup" :onConfirmDisable="onConfirmDisable">
    <template #title>
      <view class="top">
        <view class="left">
          <text class="title">选择日期</text>
          <text class="remark"
            >已选:{{
              popupBox_Content_calendar_YYYYMMDD_radio_Ref?.selectdate
            }}</text
          >
        </view>
      </view>
    </template>
    <template #content>
      <popupBox_Content_calendar_YYYYMMDD_radio
        ref="popupBox_Content_calendar_YYYYMMDD_radio_Ref"
        :date="date"
        :dateMax="dateMax"
      >
      </popupBox_Content_calendar_YYYYMMDD_radio>
    </template>
  </popupBox>
</template>
<script setup>
import { ref, computed, onMounted, watch, defineEmits } from "vue";
const popupBox_Content_calendar_YYYYMMDD_radio_Ref = ref(null);
const YYYYMM_Popup = ref(null);
const emit = defineEmits(["update:modelValue", "onConfirm"]);
const onConfirmDisable = computed(() => {
  if (popupBox_Content_calendar_YYYYMMDD_radio_Ref.value?.selectdate) {
    return false;
  } else {
    return true;
  }
});
const date = ref("");
const dateMax = ref("");
function open(params) {
  if (!params?.date || !/^\d{4}-\d{2}-\d{2}$/.test(params?.date)) {
    date.value =
      new Date().getFullYear() +
      "-" +
      (new Date().getMonth() + 1 + "").padStart(2, "0") +
      "-" +
      new Date().getDate().toString().padStart(2, "0");
  } else {
    date.value = params.date;
  }
  if (!params?.dateMax) {
    dateMax.value =
      new Date().getFullYear() +
      "-" +
      (new Date().getMonth() + 1 + "").padStart(2, "0") +
      "-" +
      new Date().getDate().toString().padStart(2, "0");
  } else if (params.dateMax == "infinite") {
    dateMax.value = params.dateMax;
  } else if (!/^\d{4}-\d{2}-\d{2}$/.test(params?.dateMax)) {
    dateMax.value =
      new Date().getFullYear() +
      "-" +
      (new Date().getMonth() + 1 + "").padStart(2, "0") +
      "-" +
      new Date().getDate().toString().padStart(2, "0");
  } else {
    dateMax.value = params.dateMax;
  }
  //打开弹窗
  YYYYMM_Popup.value.open({
    cancelText: "取消",
    confirmText: "确认",
    onOpen: function () {
      // popupBox_Content_calendar_YYYYMMDD_radio_Ref.value.selectdate = [];
    },
    onConfirm: function () {
      emit(
        "onConfirm",
        popupBox_Content_calendar_YYYYMMDD_radio_Ref.value.selectdate
      );
      if (params.onConfirm) {
        params.onConfirm(
          popupBox_Content_calendar_YYYYMMDD_radio_Ref.value.selectdate
        );
      }
      YYYYMM_Popup.value.close();
    },
  });
}

defineExpose({
  open,
});
</script>
<style scoped lang="scss">
:deep {
  .popBox {
    .content {
      // transform: scaleY(-1);
      display: flex;
    }
  }
}

.top {
  // display: flex;
  // justify-content: space-between;
  width: 100%;

  .left {
    .title {
      font-size: 34.35rpx;
      font-weight: 600;
    }

    .remark {
      margin-left: 30rpx;
      color: rgba(122, 122, 122, 1);
      font-size: 26.71rpx;
    }
  }

  .right {
  }
}
</style>

使用及效果

<template>
  <view @click="open">选日 (单选)</view>
  <!-- 日历单选 -->
  <popupBox_calendar_YYYYMMDD_radio ref="YYYYMMDD_Popup_radio">
  </popupBox_calendar_YYYYMMDD_radio>
</template>

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

image.png

多选

结构

image.png

popupBox_Content_calendar_YYYYMMDD_MultipleChoice.vue

<template>
  <view class="box">
    <view class="week">
      <view
        v-for="item in ['一', '二', '三', '四', '五', '六', '日']"
        class="item"
        >{{ item }}</view
      >
    </view>
    <view style="height: calc(100% - 80rpx); 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"
      >
        <!-- <scroll-view
        scroll-y
        style="height: 100%"
        :scroll-top="scrollTop"
        upper-threshold="0"
        lower-threshold="200"
        @scrolltolower="scrolltolower"
        @scrolltoupper="scrolltoupper"
      > -->
        <view v-for="months in dateArr">
          <view
            class="month"
            v-if="
              dateShow2(`${months.year}-${(months.month + '').padStart(2, 0)}`)
            "
            :id="`item_${months.year}-${(months.month + '').padStart(2, 0)}`"
            >{{ `${months.year}年-${months.month}月` }}</view
          >
          <view v-for="days in months.days" class="lineBox">
            <view
              v-for="day in days"
              :class="[
                'item',
                `Select_firstAndlast_${isSelect(day, months.month)}`,
                `Select_middle_${isSelectMiddle(day, months.month)}`,
              ]"
              @click="getday(day, months.month)"
              v-if="dateShow(days[0])"
            >
              <view class="value" v-if="dateShow(day)">{{
                dayFormat(day, months.month)
              }}</view>
            </view>
          </view>
        </view>
      </scroll-view>
    </view>
  </view>
</template>

<script setup>
import { ref, onMounted, computed, nextTick, defineProps } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app";
const props = defineProps({
  date: {
    type: Array,
    default: [],
    // validator: (value) => {
    //   if (!value || !/^\d{4}-\d{2}-\d{2}$/.test(value)) {
    //     return false; // 日期格式不正确
    //   }
    //   return true; // 日期格式正确
    // },
  },
  dateMax: {
    type: String,
    default:
      new Date().getFullYear() +
      "-" +
      (new Date().getMonth() + 1 + "").padStart(2, "0") +
      "-" +
      new Date().getDate().toString().padStart(2, "0"),
    validator: (value) => {
      if (value == "infinite") {
        return true;
      }
      if (!value || !/^\d{4}-\d{2}-\d{2}$/.test(value)) {
        return false; // 日期格式不正确
      }
      return true; // 日期格式正确
    },
  },
});
//传入年 获取整年的日历数组
function getdateArr(year) {
  function getdays(year, month) {
    function getDatesBetween(rangeStart, rangeEnd) {
      let dates = [];
      let currentDate = new Date(rangeStart);
      while (currentDate <= rangeEnd) {
        dates.push(currentDate.toISOString().slice(0, 10));

        currentDate.setDate(currentDate.getDate() + 1);
      }
      return dates;
    }
    function getDaysOfMonth(year, month) {
      let startDay = new Date(year, month - 1, 1);
      let endDay = new Date(year, month, 0);
      // 找到第一个星期日
      while (startDay.getDay() !== 2) {
        startDay.setDate(startDay.getDate() - 1);
      }
      // 找到最后一个星期六
      while (endDay.getDay() !== 1) {
        endDay.setDate(endDay.getDate() + 1);
      }
      return getDatesBetween(startDay, endDay);
    }
    let daysOfMonth = getDaysOfMonth(year, month);
    //分割为7个一组
    function groupDates(dates) {
      let groupedArray = [];
      let groupCount = Math.ceil(dates.length / 7);
      for (let i = 0; i < groupCount; i++) {
        let startIndex = i * 7;
        let endIndex = (i + 1) * 7;
        let group = dates.slice(startIndex, endIndex);
        groupedArray.push(group);
      }
      return groupedArray;
    }
    let group = groupDates(daysOfMonth);
    return group;
  }
  const months = [];
  for (let month = 0; month < 12; month++) {
    const monthValue = month + 1;
    months.push({
      year: year,
      month: monthValue,
      days: getdays(year, monthValue),
    });
  }
  return months;
}
const dateArr = ref([]);
onLoad(() => {
  let year = new Date().getFullYear();
  let month = (new Date().getMonth() + 1 + "").padStart(2, "0");
  if (props.date.length) {
    year = Number(props.date[1].slice(0, 4));
    month = props.date[1].slice(5, 7);
  }
  dateArr.value = getdateArr(year);
  setTimeout(() => {
    scrollToElement(year + "-" + month);
  }, 500);
});
//到顶加载上一年数据
function scrolltoupper() {
  const year = dateArr.value[0].year - 1;
  let newdate = getdateArr(year);
  setTimeout(() => {
    scrollToElement(`${year + 1}-12`);
    dateArr.value = newdate.concat(dateArr.value);
  }, 500);
}
//下拉加载上一年数据
const refresherTriggered = ref(false);
function pullDownRefresh() {
  refresherTriggered.value = true;
  const year = dateArr.value[0].year - 1;
  let newdate = getdateArr(year);
  setTimeout(() => {
    scrollToElement(`${year + 1}-12`);
    refresherTriggered.value = false;
    dateArr.value = newdate.concat(dateArr.value);
  }, 500);
}
// 到底加载下一年数据
function scrolltolower() {
  if (
    props.dateMax !== "infinite" &&
    dateArr.value[dateArr.value.length - 1].year == props.dateMax.slice(0, 4)
  ) {
    return;
  }
  const year = dateArr.value[dateArr.value.length - 1].year + 1;
  let newdate = getdateArr(year);
  dateArr.value = dateArr.value.concat(newdate);
}
//日期格式化
function dayFormat(date, month) {
  if (new Date(date).getMonth() + 1 !== month) {
    return "";
  }
  let day = new Date(date).getDate();
  return day;
}
//日期显示与否
function dateShow(day) {
  if (
    props.dateMax !== "infinite" &&
    Number(props.dateMax.replace(/-/g, "")) < Number(day.replace(/-/g, ""))
  ) {
    return false;
  }
  return true;
}
function dateShow2(day) {
  if (
    props.dateMax !== "infinite" &&
    Number(props.dateMax.substring(0, 8).replace(/-/g, "")) <
      Number(day.replace(/-/g, ""))
  ) {
    return false;
  }
  return true;
}
//选择该项
const selectdate = ref(props.date);
function getday(date, month) {
  const isInSameMonth = dayFormat(date, month);
  if (!isInSameMonth) {
    return;
  }
  const isSmallerThanDateMax = dateShow(date);
  if (!isSmallerThanDateMax) {
    return;
  }
  if (selectdate.value.length == 2 || selectdate.value.length == 0) {
    selectdate.value = [date];
    return;
  }
  if (selectdate.value.length == 1) {
    const datenum = Number(selectdate.value[0].split("-").join(""));
    const getdatenum = Number(date.split("-").join(""));
    if (datenum <= getdatenum) {
      selectdate.value.push(date);
    }
    if (datenum > getdatenum) {
      selectdate.value.unshift(date);
    }
  }
}
//判断该项是否选中
function isSelect(date, month) {
  if (new Date(date).getMonth() + 1 !== month) {
    return false;
  }
  if (selectdate.value.includes(date)) {
    return true;
  }
}
function isSelectMiddle(date, month) {
  if (new Date(date).getMonth() + 1 !== month) {
    return false;
  }
  if (selectdate.value.length == 0 || selectdate.value.length == 1) {
    return false;
  }
  const getdatenum = Number(date.split("-").join(""));
  const lastdatenum = Number(selectdate.value[1].split("-").join(""));
  const startdatenum = Number(selectdate.value[0].split("-").join(""));
  if (startdatenum < getdatenum && getdatenum < lastdatenum) {
    return true;
  }
}
//跳转到指定的年月
const scrollTop = ref(0);
function scrollToElement(domid) {
  /*#ifdef APP-PLUS*/
  uni
    .createSelectorQuery()
    .select("#item_" + domid)
    .boundingClientRect((rect) => {
      if (rect) {
        scrollTop.value = rect.top - 250;
      }
    })
    .exec();
  /*#endif*/
  /*#ifdef H5*/
  document.querySelector("#item_" + domid).scrollIntoView(true);
  /*#endif*/
}
defineExpose({
  selectdate,
});
</script>

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

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

.lineBox {
  display: flex;
  justify-content: space-between;
  align-items: center;

  .item {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 80rpx;
    height: 80rpx;
  }

  .Select_firstAndlast_true {
    position: relative;

    &:before {
      content: "";
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      height: 75rpx;
      width: 75rpx;
      border-radius: 50%;
      background-color: #337cfa;
    }

    .value {
      color: #fff;
      z-index: 9;
    }
  }
  .Select_middle_true {
    position: relative;

    &:before {
      content: "";
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      height: 75rpx;
      width: 100%;
      background-color: rgba(246, 247, 252, 1);
    }

    .value {
      // color: #fff;
      z-index: 9;
    }
    // background:red
  }
}

.years {
  .item {
    font-size: 23rpx;
    background-color: #337cfa;
    color: #fff;
    display: flex;
    justify-content: center;
    align-items: center;
    border-radius: 50%;
    padding: 5rpx;
    margin-top: 20rpx;
  }
}
.week {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-bottom: 30rpx;
  border-bottom: 1px solid rgba(217, 217, 217, 1);
  .item {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 80rpx;
  }
}
</style>

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

<template>
  <popupBox ref="YYYYMM_Popup" :onConfirmDisable="onConfirmDisable">
    <template #title>
      <view class="top">
        <view class="left">
          <text class="title">选择日期</text>
          <text class="remark"
            >已选:{{
              popupBox_Content_calendar_YYYYMMDD_MultipleChoice_Ref
                ?.selectdate[0] || "开始日期"
            }}
            ~
            {{
              popupBox_Content_calendar_YYYYMMDD_MultipleChoice_Ref
                ?.selectdate[1] || "结束日期"
            }}</text
          >
        </view>
      </view>
    </template>
    <template #content>
      <popupBox_Content_calendar_YYYYMMDD_MultipleChoice
        ref="popupBox_Content_calendar_YYYYMMDD_MultipleChoice_Ref"
        :date="date"
        :dateMax="dateMax"
      >
      </popupBox_Content_calendar_YYYYMMDD_MultipleChoice>
    </template>
  </popupBox>
</template>
<script setup>
import { ref, computed, onMounted, watch, defineEmits } from "vue";
const popupBox_Content_calendar_YYYYMMDD_MultipleChoice_Ref = ref(null);
const YYYYMM_Popup = ref(null);
const emit = defineEmits(["update:modelValue", "onConfirm"]);
const onConfirmDisable = computed(() => {
  if (
    popupBox_Content_calendar_YYYYMMDD_MultipleChoice_Ref.value?.selectdate
      .length == 2
  ) {
    return false;
  } else {
    return true;
  }
});
const date = ref([]);
const dateMax = ref("");
function open(params) {
  date.value = params?.date || [];
  if (!params?.dateMax) {
    dateMax.value =
      new Date().getFullYear() +
      "-" +
      (new Date().getMonth() + 1 + "").padStart(2, "0") +
      "-" +
      new Date().getDate().toString().padStart(2, "0");
  } else if (params.dateMax == "infinite") {
    dateMax.value = params.dateMax;
  } else if (!/^\d{4}-\d{2}-\d{2}$/.test(params?.dateMax)) {
    dateMax.value =
      new Date().getFullYear() +
      "-" +
      (new Date().getMonth() + 1 + "").padStart(2, "0") +
      "-" +
      new Date().getDate().toString().padStart(2, "0");
  } else {
    dateMax.value = params.dateMax;
  }
  //打开弹窗
  YYYYMM_Popup.value.open({
    cancelText: "取消",
    confirmText: "确认",
    onOpen: function () {
      // popupBox_Content_calendar_YYYYMMDD_MultipleChoice_Ref.value.selectdate = [];
    },
    onConfirm: function () {
      emit(
        "onConfirm",
        popupBox_Content_calendar_YYYYMMDD_MultipleChoice_Ref.value.selectdate
      );
      if (params?.onConfirm) {
        params.onConfirm(
          popupBox_Content_calendar_YYYYMMDD_MultipleChoice_Ref.value.selectdate
        );
      }
      YYYYMM_Popup.value.close();
    },
  });
}

defineExpose({
  open,
});
</script>
<style scoped lang="scss">
:deep {
  .popBox {
    .content {
      // transform: scaleY(-1);
      display: flex;
    }
  }
}

.top {
  // display: flex;
  // justify-content: space-between;
  width: 100%;

  .left {
    .title {
      font-size: 34.35rpx;
      font-weight: 600;
    }

    .remark {
      margin-left: 30rpx;
      color: rgba(122, 122, 122, 1);
      font-size: 26.71rpx;
    }
  }

  .right {
  }
}
</style>

使用及效果

<template>
  <view @click="open">选日 (多选)</view>
  <!-- 日历多选 -->
  <popupBox_calendar_YYYYMMDD_MultipleChoice
    @onConfirm="YYYYMMDD_Popup_onConfirm"
    ref="YYYYMMDD_Popup_MultipleChoice"
  >
  </popupBox_calendar_YYYYMMDD_MultipleChoice>
</template>

<script setup>
import { ref, onMounted, computed } from "vue";
const date = ref(["2023-12-26", "2023-12-27"]);
const YYYYMMDD_Popup_MultipleChoice = ref(null);
function open() {
  YYYYMMDD_Popup_MultipleChoice.value.open({
    date: date.value,
    dateMax: "2023-12-28",//如果infinite为无限制
    onConfirm: (value) => {
      date.value = value;
    },
  });
}
</script>

image.png