<script>
import dayjs from "./day";
import {
  monthsChinese,
  weeksChinese,
  closable,
  left,
  left_double,
} from "./utils";
import KButton from "../k-bottom-button/KBottomButton.vue";
import KBottomPopup from "../k-bottom-popup/KBottomPopup.vue";

export default {
  components: {KBottomPopup, KButton},
  options: {
    virtualHost: true,
  },
  // #ifndef VUE3
  model: {
    prop: "value",
    event: "input",
  },
  // #endif
  props: {
    // #ifndef VUE3
    value: {
      type: Boolean,
      default: false,
    },
    // #endif
    confirmText: {
      type: String,
      default: "确定",
    },
    title: {
      type: String,
      default: "请选择",
    },
    // #ifdef VUE3
    modelValue: {
      type: Boolean,
      default: false,
    },
    // #endif
    defaultValue: {
      type: [String, Number, Array],
    },
    type: {
      type: String, //"day" | "month" | "year"
      default: "day",
    },
    /** 限制开始时间 **/
    limitStartDate: {
      type: [String, Number],
    },
    /** 限制结束时间 **/
    limitEndDate: {
      type: [String, Number],
    },
    /** 格式化数据 **/
    formatter: {
      type: String,
    },
    /** 是否为范围选择器 **/
    range: {
      type: Boolean,
      default: () => false,
    },
    /** 是否为多选 **/
    multiple: {
      type: Boolean,
      default: () => false,
    },
    isDisabled: {
      type: Function,
    }
  },
  computed: {
    briefDate() {
      if (this.type === "day") {
        return this.currentDay.format("YYYY年MM月");
      }
      if (this.type === "month") {
        return this.currentDay.format("YYYY年");
      }
      if (this.type === "year") {
        return `${this.startYear.format("YYYY")} - ${this.startYear.add(9, "year").format("YYYY")}`;
      }
    },
    selectedDate() {
      const formatMap = {
        day: "YYYY-MM-DD",
        month: "YYYY-MM",
        year: "YYYY年",
      };
      const format = formatMap[this.type];
      if (this.range) {
        return `${this.rangeStart ? this.rangeStart.format(format) : "-"} 至 ${this.rangeEnd ? this.rangeEnd.format(format) : "-"}`;
      } else {
        if (this.checked.length > 0) {
          return this.checked.map((item) => item.format(format)).join("、");
        } else {
          return "-";
        }
      }
    },
    backText() {
      if (this.type === "day") {
        return this.currentDay.format("M");
      }
      return "";
    },
    blocks() {
      const dates =
        this.type === "day"
          ? this.days
          : this.type === "month"
            ? this.months
            : this.years;
      const result = [];
      const chineseMap = {
        day: (date) => {
          return date.format("D");
        },
        month: (date) => {
          return monthsChinese[date.month()];
        },
        year: (date) => {
          return date.format("YYYY年");
        },
      };
      for (const date of dates) {
        if (!date) {
          result.push(date);
        } else {
          const obj = {
            date,
            chinese: chineseMap[this.type](date),
            isLimit: this.isDisabled ? this.isDisabled(date.startOf(this.type).valueOf()) : ((this.limitStartDate &&
                date.isBefore(dayjs(this.limitStartDate).startOf(this.type))) ||
              (this.limitEndDate && date.isAfter(dayjs(this.limitEndDate).endOf(this.type)))),
            isCurrent: date
              .startOf(this.type)
              .isSame(this.sameDay.startOf(this.type)),
            isSelect:
              this.checked.findIndex(
                (item) => item.valueOf() === date.valueOf(),
              ) !== -1,
            isStart:
              this.rangeStart &&
              date
                .startOf(this.type)
                .isSame(this.rangeStart.startOf(this.type)),
            isEnd:
              this.rangeEnd &&
              date.startOf(this.type).isSame(this.rangeEnd.startOf(this.type)),
            isInRange:
              this.rangeStart &&
              this.rangeEnd &&
              date
                .startOf(this.type)
                .isAfter(this.rangeStart.startOf(this.type)) &&
              date
                .startOf(this.type)
                .isBefore(this.rangeEnd.startOf(this.type)),
          };
          // 若果既是范围起点又是范围终点，那么设置为选中
          if (this.range) {
            obj.isSelect = obj.isStart && obj.isEnd;
          }
          result.push(obj);
        }
      }
      return result;
    },
    days() {
      const result = [];
      let start = this.currentDay.startOf("month");
      const end = this.currentDay.endOf("month");
      //计算当前月的第一天是星期几，然后在result头部补上空字符串
      const firstDayWeek = start.day();
      for (let i = 0; i < firstDayWeek; i++) {
        result.push("");
      }
      while (start.isBefore(end)) {
        result.push(start);
        start = start.add(1, "day");
      }
      return result;
    },
    months() {
      const result = [];
      for (let i = 0; i < 12; i++) {
        const date = this.currentDay.startOf("year").add(i, "month");
        result.push(date);
      }
      return result;
    },
    startYear() {
      return this.currentDay.subtract(this.currentDay.year() % 10, "year");
    },
    years() {
      // 根据currentDay生成10个年份，如果是当前年则加上is-current
      const result = [];
      // 从startYear开始生成一个10年的数组
      for (let i = 0; i < 10; i++) {
        const date = this.startYear.add(i, "year");
        result.push(date);
      }
      return result;
    },
  },
  data() {
    return {
      sameDay: dayjs(), // 当日
      rangeStart: null, // 范围选择器开始日期
      rangeEnd: null, // 范围选择器结束日期
      checked: [], // 选中的日期
      currentDay: dayjs(), // 当前选择器显示的日期
      weeksChinese,
      left,
      closable,
      left_double,
      show: false,
    };
  },
  methods: {
    onClick() {
      if (this.range) {
        const _rangeStart = this.rangeStart
          ? this.formatter
            ? this.rangeStart.format(this.formatter)
            : this.rangeStart.valueOf()
          : null;
        const _rangeEnd = this.rangeEnd
          ? this.formatter
            ? this.rangeEnd.format(this.formatter)
            : this.rangeEnd.endOf(this.type).valueOf()
          : null;
        this.$emit("change", [_rangeStart, _rangeEnd]);
      } else {
        const _checked =
          this.selectedDate !== "-"
            ? this.multiple
              ? this.checked.map((item) =>
                this.formatter ? item.format(this.formatter) : item.valueOf(),
              )
              : this.formatter
                ? this.checked[0].format(this.formatter)
                : this.checked[0].valueOf()
            : null;
        this.$emit("change", _checked);
      }
    },
    handleClickItem(block) {
		this.show = true
      /**当day为空字符串或者在限制开始时间之前，限制开始时间之后 不允许点击 **/
      if (
        block === "" ||
        (this.isDisabled ? this.isDisabled(block.date.startOf(this.type).valueOf()) : (
          (this.limitEndDate && block.date.isAfter(dayjs(this.limitEndDate).endOf(this.type))) ||
          (this.limitStartDate && block.date.isBefore(dayjs(this.limitStartDate).startOf(this.type)))
        ))
      )
        return;
      if (this.range) {
        //如果有结束日期和开始日期，并且点击的不是开始日期和结束日期，则将其设置为开始日期并清除结束日期
        if (
          this.rangeStart &&
          this.rangeEnd &&
          !block.isStart &&
          !block.isEnd
        ) {
          this.rangeStart = block.date;
          this.rangeEnd = null;
          return;
        }
        // 如果点击的是选择的日期，那么就清除这些选项
        if (block.isSelect) {
          this.rangeStart = null;
          this.rangeEnd = null;
          return;
        }

        // 如果重复点击开始日期或者结束日期则取消选择
        if (block.isStart) {
          // 如果没有结束日期 那么将结束日期设置为开始日期
          if (!this.rangeEnd) {
            this.rangeEnd = block.date;
            return;
          }
          this.rangeStart = null;
          return;
        }
        if (block.isEnd) {
          this.rangeEnd = null;
          return;
        }
        const setStartAndEndDay = (start) => {
          if (block.date.isBefore(start)) {
            this.rangeEnd = start;
            this.rangeStart = block.date;
          } else {
            this.rangeEnd = block.date;
          }
        };
        if (this.rangeStart) {
          setStartAndEndDay(this.rangeStart);
        } else {
          // 如果有结束日期，需要判断是否在结束日期之前
          if (this.rangeEnd) {
            setStartAndEndDay(this.rangeEnd);
          } else {
            this.rangeStart = block.date;
          }
        }
      } else {
        // 如果重复点击则取消选择
        if (block.isSelect) {
          this.checked = this.checked.filter(
            (item) => item.valueOf() !== block.date.valueOf(),
          );
          return;
        }
        this.multiple
          ? this.checked.push(block.date)
          : (this.checked = [block.date]);
      }
    },
    toggle(type, direction) {
      if (this.type === "day") {
        this.currentDay = this.currentDay.add(
          direction === "left" ? -1 : 1,
          type === "single" ? "month" : "year",
        );
      }
      if (this.type === "month") {
        this.currentDay = this.currentDay.add(
          direction === "left" ? -1 : 1,
          "year",
        );
      }
      if (this.type === "year") {
        this.currentDay = this.currentDay.add(
          direction === "left" ? -10 : 10,
          "year",
        );
      }
    },
    close() {
      // #ifndef VUE3
      this.$emit("input", false);
      // #endif
      // #ifdef VUE3
      this.$emit("update:modelValue", false);
      // #endif
    },
    init() {
      // 检查类型是否为 day | month | year
      if (!["day", "month", "year"].includes(this.type)) {
        console.error("类型必须为 day | month | year");
        return;
      }
      // 检查 isRange和 multiple 是否同时为 true
      if (this.range && this.multiple) {
        console.error("目前暂不支持时间段的多选");
        return;
      }

      // 清空原数据
      this.checked = [];
      this.rangeStart = null;
      this.rangeEnd = null;
      // 初始化数据
      if (!this.defaultValue) return;
      if (this.range) {
        if (
          !Array.isArray(this.defaultValue) ||
          this.defaultValue.length !== 2
        ) {
          console.error("区间选择器的默认值必须为长度为2的数组");
          return;
        }
        this.currentDay = this.defaultValue[0]
          ? dayjs(this.defaultValue[0])
          : dayjs();
        this.defaultValue[0] && (this.rangeStart = dayjs(this.defaultValue[0]));
        this.defaultValue[1] && (this.rangeEnd = dayjs(this.defaultValue[1]));
      } else {
        if (this.multiple && Array.isArray(this.defaultValue)) {
          this.checked = this.defaultValue.map((item) => dayjs(item));
        } else {
          this.checked = [dayjs(this.defaultValue)];
        }
        this.currentDay = this.checked[0] ? dayjs(this.checked[0]) : dayjs();
      }
    },
  },
  mounted() {
    this.init();
  },
  watch: {
    // #ifdef VUE3
    modelValue: {
      handler(value) {
        this.show = !!value;
      },
    },
    // #endif
    // #ifndef VUE3
    value: {
      handler(value) {
        this.show = !!value;
      },
    },
    // #endif
    show: {
      handler(value) {
        // #ifdef VUE3
        this.$emit("update:modelValue", value);
        // #endif
        // #ifndef VUE3
        this.$emit("input", value);
        // #endif
      },
    },
    range: {
      handler() {
        this.init();
      },
    },
    multiple: {
      handler() {
        this.init();
      },
    },
    type: {
      handler() {
        this.init();
      },
    },
    defaultValue: {
      handler() {
        this.init();
      },
      deep: true,
    },
  },
};
</script>

<template>
  <KBottomPopup v-model="show">
    <view class="k-date-picker">
      <view class="inner-top">
        <view>
          <slot name="left">
            <text>{{ title }}</text>
          </slot>
        </view>
        <view @click="close">
          <slot name="right">
            <image :src="closable" class="image-closable"></image>
          </slot>
        </view>
      </view>
      <view class="inner-body">
        <view class="inner-body__arrow">
          <image
            v-if="type === 'day'"
            class="image-arrow"
            :src="left_double"
            @click="toggle('double', 'left')"
          />
          <image
            class="image-arrow"
            :src="left"
            @click="toggle('single', 'left')"
          />
          <view class="brief">{{ briefDate }}</view>
          <image
            class="image-arrow right-arrow"
            :src="left"
            @click="toggle('single', 'right')"
          />
          <image
            v-if="type === 'day'"
            class="image-arrow right-arrow"
            :src="left_double"
            @click="toggle('double', 'right')"
          />
        </view>
        <view class="inner-body__calendar">
          <view
            class="inner-body__calendar_week"
            :style="{ columnGap: range ? '0' : '12rpx' }"
            v-if="type === 'day'"
          >
            <view
              class="inner-body__calendar_week--item"
              v-for="(item, index) in weeksChinese"
              :key="index"
            >{{ item }}
            </view
            >
          </view>
          <view
            class="inner-body__calendar_block"
            :class="{
              'is-day': type === 'day',
              'is-month': type === 'month',
              'is-year': type === 'year',
            }"
            :data-text="backText"
            :style="{ columnGap: range ? '0' : '12rpx' }"
          >
            <view
              class="inner-body__calendar_block--item"
              :class="{
                'is-current': block.isCurrent,
                'is-start': block.isStart,
                'is-end': block.isEnd,
                'is-in-range': block.isInRange,
                'is-selected': block.isSelect,
                'is-limit': block.isLimit,
              }"
              v-for="(block, index) in blocks"
              :key="index"
              @click="handleClickItem(block)"
            >
              {{ block.chinese || "" }}
            </view>
          </view>
        </view>
        <!--        多选由于需要显示的数据可能会较多，这里不再就多选的数据进行展示，需要显示可以自己处理展示方式-->
        <view class="selected-wrap" v-if="!multiple">
          <view class="selected-date">
            {{ selectedDate }}
          </view>
        </view>
      </view>
    </view>
    <KButton
      :label="confirmText"
      :box-shadow="false"
      :safe-area="true"
      @onClick="onClick"
    ></KButton>
  </KBottomPopup>
</template>

<style lang="scss">
.k-date-picker {
  /** 控制提示文字的位置 **/
  --bottom-offset: 10%;
  /** 控制提示文字的大小 **/
  --bottom-font-size: 18rpx;
  /** 选中时字体的颜色**/
  --seletct--text-color: white;
  /** 选中时背景颜色 **/
  --seletct--background-color: #005ceeff;
  /** 处于区间的字体颜色 **/
  --in-range--text-color: #005ceeff;
  /** 处于区间的背景颜色 **/
  --in-range--background-color: #f2f6fc;
  /** 限制的字体颜色 **/
  --limit--text-color: #a8abb2;
  /** 限制的背景颜色 **/
  --limit--background-color: #f5f7fa;
  /** current的背景颜色 **/
  --current--background-color: #e4edfe;
  /** current的字体颜色 **/
  --current--text-color: #7994b2;

  box-sizing: border-box;
  width: 100%;
  padding: 30rpx 30rpx 20rpx;
  background-color: white;
  border-radius: 10px 10px 0 0;
  position: relative;
  display: flex;
  flex-direction: column;

  .is-current {
    border-radius: 8rpx;
    background-color: var(--current--background-color);
    position: relative;
    --color: var(--current--text-color);

    &:after {
      color: var(--color);
      position: absolute;
      left: 50%;
      bottom: var(--bottom-offset);
      transform: translate(-50%);
      font-size: var(--bottom-font-size);
      line-height: var(--bottom-font-size);
      /* #ifdef APP-PLUS */
      font-weight: 500;
      /* #endif */
    }
  }

  .is-day {
    grid-template-columns: repeat(7, minmax(0, 1fr));

    .is-current:after {
      content: "本日";
    }

    .is-start:after {
      content: "开始";
    }

    .is-end:after {
      content: "结束";
    }
  }

  .is-month {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    margin-top: 24rpx;

    .is-current:after {
      content: "本月";
    }

    .is-start:after {
      content: "开始";
    }

    .is-end:after {
      content: "结束";
    }
  }

  .is-year {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    margin-top: 24rpx;

    .is-current:after {
      content: "本年";
    }

    .is-start:after {
      content: "开始";
    }

    .is-end:after {
      content: "结束";
    }
  }

  .inner-top {
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    align-items: center;
    padding-bottom: 30rpx;
  }

  .inner-body {
    &__arrow {
      display: flex;
      flex-direction: row;
      align-items: center;
      justify-content: center;
    }

    &__calendar {
      &_week {
        display: grid;
        grid-template-columns: repeat(7, minmax(0, 1fr));

        &--item {
          /* #ifdef MP-WEIXIN */
          font-weight: bolder;
          /* #endif */
          /* #ifdef APP-PLUS */
          font-weight: 500;
          /* #endif */
          color: #303133;
          font-size: 30rpx;
          display: flex;
          flex-direction: column;
          align-items: center;
          justify-content: center;
          height: 88rpx;
        }
      }

      &_block {
        &:after {
          display: block;
          content: attr(data-text);
          color: #eef2f8ff;
          font-size: 280rpx;
          position: absolute;
          left: 50%;
          top: 50%;
          transform: translate(-50%, -50%);
          z-index: 1;
        }

        position: relative;
        display: grid;
        row-gap: 20rpx;

        &--item {
          z-index: 2;
          font-size: 30rpx;
          color: #7994b2;
          display: flex;
          flex-direction: column;
          align-items: center;
          justify-content: center;
          aspect-ratio: 1;
        }
      }
    }
  }

  .is-limit {
    color: var(--limit--text-color) !important;
    background-color: var(--limit--background-color);
  }

  .is-in-range {
    --color: var(--in-range--text-color) !important;
    background-color: var(--in-range--background-color) !important;
    border-radius: 0 !important;
    color: var(--in-range--text-color) !important;
  }

  .is-selected {
    &:after {
      content: "已选" !important;
      color: var(--seletct--text-color) !important;
      position: absolute;
      left: 50%;
      bottom: var(--bottom-offset);
      transform: translate(-50%);
      font-size: var(--bottom-font-size);
      line-height: var(--bottom-font-size);
      /* #ifdef APP-PLUS */
      font-weight: 500;
      /* #endif */
    }

    position: relative;
    background-color: var(--seletct--background-color) !important;
    color: var(--seletct--text-color) !important;
    border-radius: 8rpx !important;
  }

  .is-start {
    &:after {
      content: "开始";
      color: var(--seletct--text-color) !important;
      position: absolute;
      left: 50%;
      bottom: var(--bottom-offset);
      transform: translate(-50%);
      font-size: var(--bottom-font-size);
      line-height: var(--bottom-font-size);
      /* #ifdef APP-PLUS */
      font-weight: 500;
      /* #endif */
    }

    position: relative;
    background-color: var(--seletct--background-color) !important;
    color: var(--seletct--text-color) !important;
    border-radius: 8rpx 0 0 8rpx;
  }

  .is-end {
    &:after {
      content: "结束";
      color: var(--seletct--text-color) !important;
      position: absolute;
      left: 50%;
      bottom: var(--bottom-offset);
      transform: translate(-50%);
      font-size: var(--bottom-font-size);
      line-height: var(--bottom-font-size);
      /* #ifdef APP-PLUS */
      font-weight: 500;
      /* #endif */
    }

    position: relative;
    background-color: var(--seletct--background-color) !important;
    color: var(--seletct--text-color) !important;
    border-radius: 0 8rpx 8rpx 0;
  }

  .image-closable {
    width: 36rpx;
    height: 36rpx;
  }

  .image-arrow {
    width: 36rpx;
    height: 36rpx;
  }

  .right-arrow {
    transform: rotate(180deg);
  }

  .brief {
    margin: 0 30rpx;
    color: #000000d9;
    font-size: 30rpx;
    /* #ifdef MP-WEIXIN */
    font-weight: bolder;
    /* #endif */
    /* #ifdef APP-PLUS */
    font-weight: 500;
    /* #endif */
  }

  .selected-date {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    padding: 10rpx 0;
    color: #94a0b4ff;
    font-size: 30rpx;
    margin-top: 20rpx;
    width: 600rpx;
  }

  .selected-wrap {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    width: 100%;
  }
}
</style>
