/**
 * @file DateRangePicker
 * @description 自定义日期范围时间选择器组件
 * @author fex
 */

import React from 'react';
import moment from 'moment';
import { findDOMNode } from 'react-dom';
import { Icon } from './icons';
import Overlay from './Overlay';
import { ShortCuts, ShortCutDateRange, ShortCutDate } from './DatePicker';
import Calendar, { MutableUnitOfTime } from './calendar/Calendar';
import PopOver from './PopOver';
import PopUp from './PopUp';
import { themeable, ThemeProps } from '../theme';
import { PlainObject } from '../types';
import { convertArrayValueToMoment, getPropValue, isMobile, noop, ucFirst } from '../utils/helper';
import { LocaleProps, localeable } from '../locale';
import Input from './Input';
import { getComputedStyle } from '../utils/resize-sensor';
import { isString, kebabCase, now } from 'lodash';
import type { ViewMode } from './calendar/Calendar';
import { str2function } from '../utils/api';
import Button from './Button';
import { FormulaExec, isExpression } from '../utils/formula';

export interface DateRangePickerProps extends ThemeProps, LocaleProps {
  className?: string;
  popoverClassName?: string;
  placeholder?: string;
  theme?: any;
  format: string;
  utc?: boolean;
  inputFormat?: string;
  ranges?: string | Array<ShortCuts>;
  clearable?: boolean;
  minDate?: moment.Moment;
  maxDate?: moment.Moment;
  minDuration?: moment.Duration;
  maxDuration?: moment.Duration;
  joinValues: boolean;
  delimiter: string;
  value?: any;
  onChange: (value: any) => void;
  data?: any;
  disabled?: boolean;
  closeOnSelect?: boolean;
  overlayPlacement: string;
  timeFormat?: string;
  resetValue?: any;
  popOverContainer?: any;
  dateFormat?: string;
  embed?: boolean;
  viewMode?: 'days' | 'months' | 'years' | 'time' | 'quarters';
  borderMode?: 'full' | 'half' | 'none';
  useMobileUI?: boolean;
  initial?: string;
  type?: string;
  onRef?: any;
  /** 是否开启游标动画 */
  animation?: boolean;
  /** 日期处理函数，通常用于自定义处理绑定日期的值 */
  transform?: string;
  store?: any;
  label?: string;
  domicile?: any;
  name?: string;
}

export interface DateRangePickerState {
  isOpened: boolean;
  isFocused: boolean;
  /** 开始时间 */
  startDate?: moment.Moment;
  /** 结束时间 */
  endDate?: moment.Moment;
  /** 最近一次confirm的开始时间 */
  oldStartDate?: moment.Moment;
  /** 最近一次confirm的结束时间 */
  oldEndDate?: moment.Moment;
  /** 当前编辑的时间类型：开始时间 ｜ 结束时间 */
  editState?: 'start' | 'end';
  /** 开始时间输入值 */
  startInputValue?: string;
  /** 结束时间输入值 */
  endInputValue?: string;
  endDateOpenedFirst: boolean;
  curTimeFormat?: string;
  curDateFormat?: string;
}

export function extractWrappedText(str: string) {
  const regex = /\$\{([^}]+)\}/g; // 正则表达式，匹配 ${...} 形式的文本
  const matches = [];

  let match;
  while ((match = regex.exec(str)) !== null) {
    matches.push(match[1]); // 将匹配到的内容添加到结果数组中
  }

  return matches;
}

export const availableRanges: { [propName: string]: any } = {
  'today': {
    label: 'Date.today',
    startDate: (now: moment.Moment) => {
      return now.startOf('day');
    },
    endDate: (now: moment.Moment) => {
      return now;
    }
  },

  'yesterday': {
    label: 'Date.yesterday',
    startDate: (now: moment.Moment) => {
      return now.add(-1, 'days').startOf('day');
    },
    endDate: (now: moment.Moment) => {
      return now.add(-1, 'days').endOf('day');
    }
  },

  'tomorrow': {
    label: 'Date.tomorrow',
    startDate: (now: moment.Moment) => {
      return now.add(1, 'days').startOf('day');
    },
    endDate: (now: moment.Moment) => {
      return now.add(1, 'days').endOf('day');
    }
  },

  '1dayago': {
    label: 'DateRange.1dayago',
    startDate: (now: moment.Moment) => {
      return now.add(-1, 'days');
    },
    endDate: (now: moment.Moment) => {
      return now;
    }
  },

  // 兼容一下错误的用法
  '1daysago': {
    label: 'DateRange.1daysago',
    startDate: (now: moment.Moment) => {
      return now.add(-1, 'days');
    },
    endDate: (now: moment.Moment) => {
      return now;
    }
  },

  '7daysago': {
    label: 'DateRange.7daysago',
    startDate: (now: moment.Moment) => {
      return now.add(-7, 'days').startOf('day');
    },
    endDate: (now: moment.Moment) => {
      return now.add(-1, 'days').endOf('day');
    }
  },

  '30daysago': {
    label: 'DateRange.30daysago',
    startDate: (now: moment.Moment) => {
      return now.add(-30, 'days').startOf('day');
    },
    endDate: (now: moment.Moment) => {
      return now.add(-1, 'days').endOf('day');
    }
  },

  '90daysago': {
    label: 'DateRange.90daysago',
    startDate: (now: moment.Moment) => {
      return now.add(-90, 'days').startOf('day');
    },
    endDate: (now: moment.Moment) => {
      return now.add(-1, 'days').endOf('day');
    }
  },

  'prevweek': {
    label: 'DateRange.lastWeek',
    startDate: (now: moment.Moment) => {
      return now.startOf('week').add(-1, 'weeks');
    },
    endDate: (now: moment.Moment) => {
      return now.startOf('week').add(-1, 'days').endOf('day');
    }
  },

  'thisweek': {
    label: 'DateRange.thisWeek',
    startDate: (now: moment.Moment) => {
      return now.startOf('week');
    },
    endDate: (now: moment.Moment) => {
      return now.endOf('week');
    }
  },

  'thismonth': {
    label: 'DateRange.thisMonth',
    startDate: (now: moment.Moment) => {
      return now.startOf('month');
    },
    endDate: (now: moment.Moment) => {
      return now.endOf('month');
    }
  },

  'thisquarter': {
    label: 'DateRange.thisQuarter',
    startDate: (now: moment.Moment) => {
      return now.startOf('quarter');
    },
    endDate: (now: moment.Moment) => {
      return now.endOf('quarter');
    }
  },

  'prevmonth': {
    label: 'DateRange.lastMonth',
    startDate: (now: moment.Moment) => {
      return now.startOf('month').add(-1, 'month');
    },
    endDate: (now: moment.Moment) => {
      return now.startOf('month').add(-1, 'day').endOf('day');
    }
  },

  'prevquarter': {
    label: 'DateRange.lastQuarter',
    startDate: (now: moment.Moment) => {
      return now.startOf('quarter').add(-1, 'quarter');
    },
    endDate: (now: moment.Moment) => {
      return now.startOf('quarter').add(-1, 'day').endOf('day');
    }
  },

  'thisyear': {
    label: 'DateRange.thisYear',
    startDate: (now: moment.Moment) => {
      return now.startOf('year');
    },
    endDate: (now: moment.Moment) => {
      return now.endOf('year');
    }
  },

  'lastYear': {
    label: 'DateRange.lastYear',
    startDate: (now: moment.Moment) => {
      return now.startOf('year').add(-1, 'year');
    },
    endDate: (now: moment.Moment) => {
      return now.endOf('year').add(-1, 'year').endOf('day');
    }
  },
  // 兼容一下之前的用法 'lastYear'
  'prevyear': {
    label: 'DateRange.lastYear',
    startDate: (now: moment.Moment) => {
      return now.startOf('year').add(-1, 'year');
    },
    endDate: (now: moment.Moment) => {
      return now.endOf('year').add(-1, 'year').endOf('day');
    }
  },
};

export const advancedRanges = [
  {
    regexp: /^(\d+)hoursago$/,
    resolve: (__: any, _: string, hours: string) => {
      return {
        label: __('DateRange.hoursago', { hours }),
        startDate: (now: moment.Moment) => {
          return now.add(-hours, 'hours').startOf('hour');
        },
        endDate: (now: moment.Moment) => {
          return now.add(-1, 'hours').endOf('hours');
        }
      };
    }
  },
  {
    regexp: /^(\d+)hourslater$/,
    resolve: (__: any, _: string, hours: string) => {
      return {
        label: __('DateRange.hourslater', { hours }),
        startDate: (now: moment.Moment) => {
          return now.startOf('hour');
        },
        endDate: (now: moment.Moment) => {
          return now.add(hours, 'hours').endOf('hour');
        }
      };
    }
  },
  {
    regexp: /^(\d+)daysago$/,
    resolve: (__: any, _: string, days: string) => {
      return {
        label: __('DateRange.daysago', { days }),
        startDate: (now: moment.Moment) => {
          return now.add(-days, 'days').startOf('day');
        },
        endDate: (now: moment.Moment) => {
          return now.add(-1, 'days').endOf('day');
        }
      };
    }
  },
  {
    regexp: /^(\d+)dayslater$/,
    resolve: (__: any, _: string, days: string) => {
      return {
        label: __('DateRange.dayslater', { days }),
        startDate: (now: moment.Moment) => {
          return now.startOf('day');
        },
        endDate: (now: moment.Moment) => {
          return now.add(days, 'days').endOf('day');
        }
      };
    }
  },
  {
    regexp: /^(\d+)weeksago$/,
    resolve: (__: any, _: string, weeks: string) => {
      return {
        label: __('DateRange.weeksago', { weeks }),
        startDate: (now: moment.Moment) => {
          return now.startOf('week').add(-weeks, 'weeks');
        },
        endDate: (now: moment.Moment) => {
          return now.startOf('week').add(-1, 'days').endOf('day');
        }
      };
    }
  },
  {
    regexp: /^(\d+)weekslater$/,
    resolve: (__: any, _: string, weeks: string) => {
      return {
        label: __('DateRange.weekslater', { weeks }),
        startDate: (now: moment.Moment) => {
          return now.startOf('week');
        },
        endDate: (now: moment.Moment) => {
          return now.startOf('week').add(weeks, 'weeks').endOf('day');
        }
      };
    }
  },
  {
    regexp: /^(\d+)monthsago$/,
    resolve: (__: any, _: string, months: string) => {
      return {
        label: __('DateRange.monthsago', { months }),
        startDate: (now: moment.Moment) => {
          return now.startOf('months').add(-months, 'months');
        },
        endDate: (now: moment.Moment) => {
          return now.startOf('month').add(-1, 'days').endOf('day');
        }
      };
    }
  },
  {
    regexp: /^(\d+)monthslater$/,
    resolve: (__: any, _: string, months: string) => {
      return {
        label: __('DateRange.monthslater', { months }),
        startDate: (now: moment.Moment) => {
          return now.startOf('month');
        },
        endDate: (now: moment.Moment) => {
          return now.startOf('month').add(months, 'months').endOf('day');
        }
      };
    }
  },
  {
    regexp: /^(\d+)quartersago$/,
    resolve: (__: any, _: string, quarters: string) => {
      return {
        label: __('DateRange.quartersago', { quarters }),
        startDate: (now: moment.Moment) => {
          return now.startOf('quarters').add(-quarters, 'quarters');
        },
        endDate: (now: moment.Moment) => {
          return now.startOf('quarter').add(-1, 'days').endOf('day');
        }
      };
    }
  },
  {
    regexp: /^(\d+)quarterslater$/,
    resolve: (__: any, _: string, quarters: string) => {
      return {
        label: __('DateRange.quarterslater', { quarters }),
        startDate: (now: moment.Moment) => {
          return now.startOf('quarter');
        },
        endDate: (now: moment.Moment) => {
          return now.startOf('quarter').add(quarters, 'quarters').endOf('day');
        }
      };
    }
  },
  {
    regexp: /^(\d+)yearsago$/,
    resolve: (__: any, _: string, years: string) => {
      return {
        label: __('DateRange.yearsago', { years }),
        startDate: (now: moment.Moment) => {
          return now.startOf('years').add(-years, 'years');
        },
        endDate: (now: moment.Moment) => {
          return now.startOf('year').add(-1, 'days').endOf('day');
        }
      };
    }
  },
  {
    regexp: /^(\d+)yearslater$/,
    resolve: (__: any, _: string, years: string) => {
      return {
        label: __('DateRange.yearslater', { years }),
        startDate: (now: moment.Moment) => {
          return now.startOf('year');
        },
        endDate: (now: moment.Moment) => {
          return now.startOf('year').add(years, 'years').endOf('day');
        }
      };
    }
  }
];

const dateFormats = {
  Y: { format: 'YYYY' },
  Q: { format: 'YYYY [Q]Q' },
  M: { format: 'YYYY-MM' },
  D: { format: 'YYYY-MM-DD' }
} as Record<string, { format: string }>;

const timeFormats = {
  h: { format: 'HH' },
  H: { format: 'HH' },
  m: { format: 'mm' },
  s: { format: 'ss' },
  S: { format: 'ss' }
} as Record<string, { format: string }>;

export class DateRangePicker extends React.Component<
  DateRangePickerProps,
  DateRangePickerState
> {
  static defaultProps = {
    placeholder: 'DateRange.placeholder',
    format: 'X',
    inputFormat: 'YYYY-MM-DD',
    joinValues: true,
    clearable: true,
    delimiter: ',',
    ranges: 'yesterday,7daysago,prevweek,thismonth,prevmonth,prevquarter',
    resetValue: '',
    closeOnSelect: true,
    overlayPlacement: 'auto',
    animation: true
  };

  innerDom: any;
  popover: any;
  input?: HTMLInputElement;

  static formatValue(
    newValue: any,
    format: string,
    joinValues: boolean,
    delimiter: string,
    utc = false
  ) {
    newValue = [
      (utc ? moment.utc(newValue.startDate) : newValue.startDate)?.format(
        format
      ),
      (utc ? moment.utc(newValue.endDate) : newValue.endDate)?.format(format)
    ];

    if (joinValues) {
      newValue = newValue.join(delimiter);
    }

    return newValue;
  }

  static unFormatValue(
    value: any,
    format: string,
    joinValues: boolean,
    delimiter: string
  ) {
    if (!value) {
      return {
        startDate: undefined,
        endDate: undefined
      };
    }

    if (joinValues && typeof value === 'string') {
      value = value.split(delimiter);
    }

    return {
      startDate: value[0] ? moment(value[0], format) : undefined,
      endDate: value[1] ? moment(value[1], format) : undefined
    };
  }

  dom: React.RefObject<HTMLDivElement>;
  calendarRef: React.RefObject<HTMLDivElement>;
  nextMonth = moment().add(1, 'months').startOf('day');
  currentMonth = moment().startOf('day');
  startInputRef: React.RefObject<HTMLInputElement>;
  endInputRef: React.RefObject<HTMLInputElement>;
  separatorRef: React.RefObject<HTMLSpanElement>;

  constructor(props: DateRangePickerProps) {
    super(props);

    this.open = this.open.bind(this);
    this.close = this.close.bind(this);
    this.handleFocus = this.handleFocus.bind(this);
    this.handleBlur = this.handleBlur.bind(this);
    this.checkStartIsValidDate = this.checkStartIsValidDate.bind(this);
    this.checkEndIsValidDate = this.checkEndIsValidDate.bind(this);
    this.confirm = this.confirm.bind(this);
    this.clearValue = this.clearValue.bind(this);
    this.dom = React.createRef();
    this.startInputRef = React.createRef();
    this.endInputRef = React.createRef();
    this.separatorRef = React.createRef();
    this.calendarRef = React.createRef();
    this.handleClick = this.handleClick.bind(this);
    this.handlePopOverClick = this.handlePopOverClick.bind(this);
    this.renderDay = this.renderDay.bind(this);
    this.renderQuarter = this.renderQuarter.bind(this);
    this.handleMobileChange = this.handleMobileChange.bind(this);
    const { format, joinValues, delimiter, value, inputFormat, dateFormat, timeFormat, } = this.props;
    this.getTarget = this.getTarget.bind(this);
    const { startDate, endDate } = DateRangePicker.unFormatValue(value, format, joinValues, delimiter)
    let curDateFormat = dateFormat ?? '';
    let curTimeFormat = timeFormat ?? '';
    let curTimeFormatArr = [] as string[];
    !dateFormat &&
      Object.keys(dateFormats).forEach((item: string) => {
        if (inputFormat?.includes(item)) {
          curDateFormat = dateFormats[item].format;
        }
      });
    !timeFormat &&
      Object.keys(timeFormats).forEach((item: string) => {
        if (inputFormat?.includes(item)) {
          curTimeFormatArr.push(timeFormats[item].format);
        }
      });
    curTimeFormat = curTimeFormatArr.length
      ? curTimeFormatArr.join(':')
      : curTimeFormat;
    this.state = {
      isOpened: false,
      isFocused: false,
      startDate,
      endDate,
      editState: 'start',
      oldStartDate: startDate,
      oldEndDate: endDate,
      startInputValue: startDate?.format(inputFormat),
      endInputValue: endDate?.format(inputFormat),
      endDateOpenedFirst: false,
      curDateFormat,
      curTimeFormat
    };
  }

  componentDidMount() {
    document.body.addEventListener('click', this.handleOutClick, true);
    this.props?.onRef?.(this);
  }

  componentWillUnmount() {
    document.body.removeEventListener('click', this.handleOutClick, true);
  }

  handleOutClick = (e: Event) => {
    if (
      !e.target ||
      !this.dom.current ||
      this.dom.current.contains(e.target as HTMLElement) ||
      !this.calendarRef.current ||
      this.calendarRef.current.contains(e.target as HTMLElement)
    ) {
      return;
    }
    if (this.state.isOpened) {
      e.preventDefault();
      this.close(true);
    }
  }

  componentDidUpdate(prevProps: DateRangePickerProps, prevState: DateRangePickerState) {
    const props = this.props;
    const { value, format, joinValues, delimiter, inputFormat, dateFormat, timeFormat } = props;
    if (prevProps.inputFormat != inputFormat) {
      let curDateFormat = dateFormat ?? '';
      let curTimeFormat = timeFormat ?? '';
      let curTimeFormatArr = [] as string[];

      !dateFormat &&
        Object.keys(dateFormats).forEach((item: string) => {
          if (inputFormat?.includes(item)) {
            curDateFormat = dateFormats[item].format;
          }
        });
      !timeFormat &&
        Object.keys(timeFormats).forEach((item: string) => {
          if (inputFormat?.includes(item)) {
            curTimeFormatArr.push(timeFormats[item].format);
          }
        });

      this.setState({
        curDateFormat,
        curTimeFormat: curTimeFormatArr.length
          ? curTimeFormatArr.join(':')
          : curTimeFormat
      });
    }
    if (prevProps.value !== value) {
      const { startDate, endDate } = DateRangePicker.unFormatValue(
        value,
        format,
        joinValues,
        delimiter
      );
      this.setState({
        startDate,
        endDate,
        startInputValue:
          startDate && startDate?.isValid()
            ? startDate?.format(inputFormat)
            : '',
        endInputValue:
          endDate && endDate?.isValid()
            ? endDate?.format(inputFormat)
            : ''
      });
    }
  }

  focus() {
    if (!this.dom.current || this.props.disabled) {
      return;
    }

    this.dom.current.focus();
  }

  blur() {
    if (!this.dom.current || this.props.disabled) {
      return;
    }

    this.dom.current.blur();
  }

  handleFocus() {
    this.setState({
      isFocused: true
    });
  }

  handleBlur() {
    this.setState({
      isFocused: false
    });
  }

  open() {
    if (this.props.disabled) {
      return;
    }

    this.setState({
      isOpened: true,
      editState: 'start'
    });
  }

  close(isConfirm: boolean = false, isHandleChange?: boolean) {
    if (isConfirm) {
      /** 未点击确认关闭时，将日期恢复至未做任何选择的状态 */
      const {
        value,
        format,
        joinValues,
        delimiter,
        inputFormat
      } = this.props;
      const { startDate, endDate } = DateRangePicker.unFormatValue(
        value,
        format,
        joinValues,
        delimiter
      );
      this.setState({
        startDate,
        endDate,
        oldStartDate: startDate,
        oldEndDate: endDate,
        startInputValue:
          startDate && moment(startDate).isValid()
            ? startDate.format(inputFormat)
            : '',
        endInputValue:
          endDate && moment(endDate).isValid()
            ? endDate.format(inputFormat)
            : ''
      });
    } else {
      this.setState({
        oldStartDate: this.state.startDate,
        oldEndDate: this.state.endDate
      });
    }
    !isMobile() && this.setState({
      editState: undefined,
    })

    this.setState(
      {
        isOpened: isMobile() ? !!isHandleChange : false,
        endDateOpenedFirst: false
      },
      this.blur
    );
  }

  handleClick() {
    this.state.isOpened ? this.close() : this.open();
  }

  handlePopOverClick(e: React.MouseEvent<any>) {
    e.stopPropagation();
    e.preventDefault();
  }

  confirm(isHandleChange?: boolean) {
    const { startDate, endDate } = this.state;
    const { format, joinValues, delimiter, utc } = this.props;
    if (!startDate && !endDate) {
      return;
    } else if (endDate && startDate?.isAfter?.(endDate)) {
      return;
    }
    if (isHandleChange) return;
    this.props.onChange(
      DateRangePicker.formatValue(
        { startDate, endDate },
        format,
        joinValues,
        delimiter,
        utc
      )
    );
    if (startDate && !endDate) {
      this.setState({ editState: 'end', endDateOpenedFirst: false });
    } else {
      this.close(!isMobile());
    }
  }

  filterDate(
    date: moment.Moment,
    options: {
      type: 'start' | 'end';
      originValue?: moment.Moment;
      timeFormat?: string;
      subControlViewMode?: 'time';
      /** 自动初始化绑定值，用于首次选择且当前未绑定值，默认使用当前时间 */
      autoInitDefaultValue?: boolean;
    } = { type: 'start' }
  ): moment.Moment {
    const { type, originValue, timeFormat, subControlViewMode, autoInitDefaultValue } = options || {
      type: 'start'
    };
    let value = date.clone();
    const { transform, data } = this.props;
    const { startDate, endDate } = this.state;
    /** 此时为点选后的值初始化设置，不应该被内部转化逻辑和transformFn限制 */
    if (autoInitDefaultValue === true) {
      const now = moment();

      /** 如果已经设置了结束时间且当前时间已经超出了结束时间，则开始时间不能超过结束时间 */
      if (!startDate && endDate && type === 'start' && now.isAfter?.(endDate)) {
        value = endDate.clone();
        return value;
      }

      const timePart: Record<MutableUnitOfTime, number> = {
        date: value.get('date'),
        hour: value.get('hour'),
        minute: value.get('minute'),
        second: value.get('second'),
        millisecond: value.get('millisecond')
      };
      if (!isMobile()) {
        Object.keys(timePart).forEach((unit: MutableUnitOfTime) => {
          /** 首次选择时间，日期使用当前时间; 将未设置过的时间字段设置为当前值 */
          if (
            (unit === 'date' && subControlViewMode === 'time') ||
            (unit !== 'date' && timePart[unit] === 0)
          ) {
            timePart[unit] = now.get(unit);
          }
        });
        value.set(timePart);
      }
      return value;
    }
    const transformFn =
      transform && typeof transform === 'string'
        ? str2function(transform, 'value', 'config', 'props', 'data', 'moment')
        : transform;

    /** 日期时间选择器组件支持用户选择时间，如果用户手动选择了时间，则不需要走默认处理 */
    if (subControlViewMode && subControlViewMode === 'time') {
      return value;
    }
    // 没有初始值
    if (!originValue) {
      value = value[type === 'start' ? 'startOf' : 'endOf']('day');
    } else if (typeof timeFormat === 'string' && /ss/.test(timeFormat)) {
      value = value[type === 'start' ? 'startOf' : 'endOf']('second');
    } else if (typeof timeFormat === 'string' && /mm/.test(timeFormat)) {
      value = value[type === 'start' ? 'startOf' : 'endOf']('minute');
    } else if (typeof timeFormat === 'string' && /HH/i.test(timeFormat)) {
      value = value[type === 'start' ? 'startOf' : 'endOf']('hour');
    } else if (typeof timeFormat === 'string' && /Q/i.test(timeFormat)) {
      value = value[type === 'start' ? 'startOf' : 'endOf']('quarter');
    } else {
      value = value[type === 'start' ? 'startOf' : 'endOf']('day');
    }
    if (typeof transformFn === 'function') {
      value = transformFn(
        value,
        { type, originValue, timeFormat },
        this.props,
        data,
        moment
      );
    }
    return value;
  }

  handleMobileChange = (data: any, callback?: () => void) => {
    this.setState(
      {
        startDate: data.startDate,
        endDate: data.endDate
      },
      callback
    );
  }

  selectShortcut = (shortcut: PlainObject) => {
    const {
      closeOnSelect,
      minDate,
      maxDate
    } = this.props;
    const now = moment();
    const startDate = shortcut.startDate(now.clone());
    const endDate = shortcut.endDate(now.clone());
    this.setState(
      {
        startDate:
          minDate && minDate?.isValid()
            ? moment.max(startDate, minDate)
            : startDate,
        endDate:
          maxDate && maxDate?.isValid() ? moment.min(maxDate, endDate) : endDate
      },
      closeOnSelect ? this.confirm : noop
    );
  }

  renderRanges = (ranges: string | Array<ShortCuts> | undefined) => {
    if (!ranges) return null;
    const { classPrefix: ns, data, format, store, type, inputFormat, name } = this.props;
    let rangeArr: Array<string | ShortCuts>;
    if (typeof ranges === 'string') {
      rangeArr = ranges.split(',');
    } else {
      rangeArr = ranges;
    }
    const __ = this.props.translate;
    return (
      <div style={{ paddingBottom: '5px' }}>
        <ul className={`${ns}DateRangePicker-rangers ${type === 'input-datetime-range' ? 'is-datetime-range' : ''}`}
          style={{ height: isMobile() ? 'unset' : type?.includes('time') ? '338px' : '291px' }}>
          {rangeArr.map(item => {
            if (!item) {
              return null;
            }
            let range: PlainObject = {};
            if (typeof item === 'string') {
              if (availableRanges[item]) {
                range = availableRanges[item];
                if (!range) return null;
                range.key = item;
              } else {
                // 通过正则尝试匹配
                for (let i = 0, len = advancedRanges.length; i < len; i++) {
                  let value = advancedRanges[i];
                  const m = value.regexp.exec(item);
                  if (m) {
                    range = value.resolve.apply(item, [__, ...m]);
                    range.key = item;
                  }
                }
              }
            } else if (
              (item as ShortCutDateRange).startDate &&
              (item as ShortCutDateRange).endDate
            ) {
              const shortcutRaw = { ...item } as ShortCutDateRange;
              range = {
                ...item,
                startDate: () => {
                  const startDate = isExpression(shortcutRaw.startDate)
                    ? moment(
                      FormulaExec['formula'](shortcutRaw.startDate, data),
                      format
                    )
                    : typeof shortcutRaw.startDate === 'string'
                      ? moment(shortcutRaw.startDate, format)
                      : shortcutRaw.startDate;

                  return startDate &&
                    moment.isMoment(startDate) &&
                    startDate.isValid()
                    ? startDate
                    : (item as ShortCutDateRange).startDate;
                },
                endDate: () => {
                  const endDate = isExpression(shortcutRaw.endDate)
                    ? moment(
                      FormulaExec['formula'](shortcutRaw.endDate, data),
                      format
                    )
                    : typeof shortcutRaw.endDate === 'string'
                      ? moment(shortcutRaw.endDate, format)
                      : shortcutRaw.endDate;

                  return endDate && moment.isMoment(endDate) && endDate.isValid()
                    ? endDate
                    : (item as ShortCutDateRange).endDate;
                }
              };
            } else if ((item as ShortCutDate).date) {
              const shortcutRaw = { ...item } as ShortCutDate;
              const compareFields = extractWrappedText((shortcutRaw.date as any)?.split('DATEMODIFY(')?.[1] || '');
              let field = '';
              if (compareFields?.length) {
                field = compareFields[0]
              } else {
                return null
              }
              const compareValue = getPropValue({ data: name?.includes('.') ? store.data?.[name.split('.')[0]] : store.data, name: field })
              let tiemsArr: Array<string> = []
              if (isString(compareValue)) {
                tiemsArr = compareValue.split(',')
              }
              range = {
                ...item,
                startDate: () => {
                  const startDate = moment(
                    FormulaExec['formula']((shortcutRaw.date as any).replace(field, tiemsArr?.[0] ? 'rangeStart' : now()), { rangeStart: tiemsArr?.[0] || '' }),
                    format
                  )
                  if (type === 'input-datetime-range') {
                    const nowDate = startDate.format(inputFormat)
                    if (nowDate.split(' ')?.[0] && tiemsArr?.[0]?.split(' ')?.[1]) {
                      const newDate = [nowDate.split(' ')?.[0], tiemsArr?.[0].split(' ')?.[1]].join(' ');
                      return moment(newDate)
                    }
                  }
                  return startDate &&
                    moment.isMoment(startDate) &&
                    startDate.isValid()
                    ? startDate
                    : (item as ShortCutDate).date;
                },
                endDate: () => {
                  const endDate = moment(
                    FormulaExec['formula']((shortcutRaw.date as any).replace(field, tiemsArr?.[1] ? 'rangeEnd' : now()), { rangeEnd: tiemsArr?.[1] || '' }),
                    format
                  )
                  if (type === 'input-datetime-range') {
                    const nowDate = endDate.format(inputFormat)
                    if (nowDate.split(' ')?.[0] && tiemsArr?.[1]?.split(' ')?.[1]) {
                      const newDate = [nowDate.split(' ')?.[0], tiemsArr?.[1].split(' ')?.[1]].join(' ');
                      return moment(newDate)
                    }
                  }
                  return endDate && moment.isMoment(endDate) && endDate.isValid()
                    ? endDate
                    : (item as ShortCutDate).date;
                }
              };
            }
            if (Object.keys(range).length) {
              return (
                <li
                  className={`${ns}DateRangePicker-ranger`}
                  onClick={() => this.selectShortcut(range)}
                  key={range.key || range.label}
                >
                  <a>{__(range.label)}</a>
                </li>
              );
            } else {
              return null;
            }
          })}
        </ul>
      </div>
    );
  }

  clearValue(e: React.MouseEvent<any>) {
    e.preventDefault();
    e.stopPropagation();
    const { resetValue, onChange } = this.props;

    onChange(resetValue);
    this.setState({
      isFocused: false
    });
  }

  checkStartIsValidDate(currentDate: moment.Moment) {
    let { endDate, startDate } = this.state;
    let { minDate, maxDate, minDuration, maxDuration, viewMode } = this.props;
    const precision = viewMode === 'time' ? 'hours' : viewMode || 'day';

    maxDate =
      maxDate && endDate
        ? maxDate.isBefore?.(endDate)
          ? maxDate
          : endDate
        : maxDate || endDate;

    if (minDate && currentDate.isBefore?.(minDate, precision)) {
      return false;
    } else if (maxDate && currentDate.isAfter?.(maxDate, precision)) {
      return false;
    } else if (
      // 如果配置了 minDuration 那么 EndDate - minDuration 之后的天数也不能选
      endDate &&
      minDuration &&
      currentDate.isAfter?.(endDate.clone().subtract(minDuration))
    ) {
      return false;
    } else if (
      endDate &&
      maxDuration &&
      currentDate.isBefore?.(endDate.clone().subtract(maxDuration))
    ) {
      return false;
    }

    return true;
  }

  checkEndIsValidDate(currentDate: moment.Moment) {
    let { startDate } = this.state;
    let { minDate, maxDate, minDuration, maxDuration, viewMode } = this.props;
    const precision = viewMode === 'time' ? 'hours' : viewMode || 'day';

    minDate =
      minDate && startDate
        ? minDate.isAfter?.(startDate)
          ? minDate
          : startDate
        : minDate || startDate;

    if (minDate && currentDate.isBefore?.(minDate, precision)) {
      return false;
    } else if (maxDate && currentDate.isAfter?.(maxDate, precision)) {
      return false;
    } else if (
      startDate &&
      minDuration &&
      currentDate.isBefore?.(startDate.clone().add(minDuration))
    ) {
      return false;
    } else if (
      startDate &&
      maxDuration &&
      currentDate.isAfter?.(startDate.clone().add(maxDuration))
    ) {
      return false;
    }

    return true;
  }

  renderDay(props: any, currentDate: moment.Moment) {
    let { startDate, endDate } = this.state;

    if (
      startDate &&
      endDate &&
      currentDate.isBetween(startDate, endDate, 'day', '[]')
    ) {
      props.className += ' rdtBetween';
    }

    return <td {...props}>{currentDate.date()}</td>;
  }

  renderQuarter(props: any, quarter: number, year: number) {
    const currentDate = moment().year(year).quarter(quarter);
    const { startDate, endDate } = this.state;

    if (
      startDate &&
      endDate &&
      currentDate.isBetween(startDate, endDate, 'quarter', '[]')
    ) {
      props.className += ' rdtBetween';
    }

    return (
      <td {...props}>
        <span>Q{quarter}</span>
      </td>
    );
  }
  endInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { onChange, inputFormat, utc } = this.props;
    const value = e.currentTarget.value;
    this.setState({ endInputValue: value });
    if (value === '') {
      onChange('');
    } else {
      let newDate = this.getEndDateByDuration(
        moment(value, inputFormat)
      );
      this.setState({ endDate: newDate });
    }
  }
  // 根据 duration 修复起始时间
  getStartDateByDuration = (newValue: moment.Moment) => {
    const { minDuration, maxDuration, type } = this.props;
    let { endDate, editState } = this.state;
    if (!endDate) {
      return newValue;
    }
    // 时间范围必须统一成同一天，不然会不一致
    if (type === 'input-time-range' && endDate) {
      newValue.set({
        year: endDate.year(),
        month: endDate.month(),
        date: endDate.date()
      });
    }

    if (
      minDuration &&
      newValue.isBefore?.(endDate.clone().subtract(minDuration))
    ) {
      newValue = endDate.clone().subtract(minDuration);
    }

    if (
      maxDuration &&
      newValue.isAfter?.(endDate.clone().subtract(maxDuration))
    ) {
      newValue = endDate.clone().subtract(maxDuration);
    }

    return newValue;
  }
  // 根据 duration 修复结束时间
  getEndDateByDuration = (newValue: moment.Moment) => {
    const { minDuration, maxDuration, type, maxDate } = this.props;
    let { startDate, endDate, editState } = this.state;
    if (!startDate) {
      return newValue;
    }

    // 时间范围必须统一成同一天，不然会不一致
    if (type === 'input-time-range' && startDate) {
      newValue.set({
        year: startDate.year(),
        month: startDate.month(),
        date: startDate.date()
      });
    }

    if (minDuration && newValue.isBefore?.(startDate.clone().add(minDuration))) {
      newValue = startDate.clone().add(minDuration);
    }

    if (maxDuration && newValue.isAfter?.(startDate.clone().add(maxDuration))) {
      newValue = startDate.clone().add(maxDuration);
    }

    if (maxDate && newValue && newValue.isAfter?.(maxDate, 'second')) {
      newValue = maxDate;
    }

    return newValue;
  }
  openStart = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
    e.stopPropagation()
    if (this.props.disabled) {
      return;
    }
    this.setState({
      isOpened: true,
      editState: 'start'
    });
  }

  openEnd = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
    e.stopPropagation()
    if (this.props.disabled) {
      return;
    }
    this.setState({
      isOpened: true,
      editState: 'end',
      endDateOpenedFirst: true
    });
  }

  // 主要用于处理时间的情况
  handleTimeStartChange = (newValue: moment.Moment, isHandleChange?: boolean) => {
    const {
      embed,
      inputFormat,
      minDuration,
      maxDuration,
      minDate
    } = this.props;
    const { startDate, endDate } = this.state;
    // 时间范围必须统一成同一天，不然会不一致
    if (endDate) {
      newValue.set({
        year: endDate.year(),
        month: endDate.month(),
        date: endDate.date()
      });
    }

    if (minDate && newValue && newValue.isBefore?.(minDate, 'second')) {
      newValue = minDate;
    }
    this.setState(
      {
        startDate: newValue,
        startInputValue: newValue.format(inputFormat)
      },
      () => {
        (embed || isMobile()) && this.confirm(isHandleChange);
      }
    );
  }

  handleTimeEndChange = (newValue: moment.Moment, isHandleChange?: boolean) => {
    const {
      embed,
      inputFormat,
      minDuration,
      maxDuration,
      maxDate
    } = this.props;
    const { startDate, endDate } = this.state;
    if (startDate) {
      newValue.set({
        year: startDate.year(),
        month: startDate.month(),
        date: startDate.date()
      });
    }

    if (maxDate && newValue && newValue.isAfter?.(maxDate, 'second')) {
      newValue = maxDate;
    }

    if (
      startDate &&
      minDuration &&
      newValue.isBefore?.(startDate.clone().add(minDuration))
    ) {
      newValue = startDate.clone().add(minDuration);
    }
    if (
      startDate &&
      maxDuration &&
      newValue.isAfter?.(startDate.clone().add(maxDuration))
    ) {
      newValue = startDate.clone().add(maxDuration);
    }
    this.setState(
      {
        endDate: newValue,
        endInputValue: newValue.format(inputFormat)
      },
      () => {
        (embed || isMobile()) && this.confirm(isHandleChange);
      }
    );
  }

  handleDateChange = (newValue: moment.Moment, isHandleChange?: boolean) => {
    let { editState } = this.state;
    if (editState === 'start') {
      this.handleStartDateChange(newValue, isHandleChange);
    } else if (editState === 'end') {
      this.handelEndDateChange(newValue, isHandleChange);
    }
  }

  /**
   * @param {Moment} newValue 当前选择的日期时间值
   * @param {ViewMode=} subControlViewMode 子选择控件的类型，可选参数（'time'），用于区分datetime选择器的触发控件
   */
  handleStartDateChange = (newValue: moment.Moment, isHandleChange?: boolean) => {
    const { minDate, inputFormat, type } = this.props;
    let { startDate, endDateOpenedFirst, curTimeFormat: timeFormat } = this.state;
    if (minDate && newValue.isBefore?.(minDate)) {
      newValue = minDate;
    }

    const date = this.filterDate(newValue, {
      type: 'start',
      originValue: startDate || minDate,
      timeFormat,
      autoInitDefaultValue: !!timeFormat && newValue && !startDate
    });
    const newState = {
      startDate: date,
      startInputValue: date.format(inputFormat)
    } as any;
    if (!isMobile()) {
      // 这些没有时间的选择点第一次后第二次就是选结束时间
      if (
        !endDateOpenedFirst &&
        (type === 'input-date-range' ||
          type === 'input-year-range' ||
          type === 'input-quarter-range' ||
          type === 'input-month-range'
        )
      ) {
        newState.editState = 'end';
      }
    }
    this.setState(newState, () => { isMobile() && this.confirm(isHandleChange) });
  }

  /**
   * @param {Moment} newValue 当前选择的日期时间值
   * @param {string=} subControlViewMode 子选择控件的类型的类型，可选参数（'time'），用于区分datetime选择器的触发控件
   */
  handelEndDateChange = (newValue: moment.Moment, isHandleChange?: boolean) => {
    const { embed, inputFormat, type } = this.props;
    let {
      startDate,
      endDate,
      endDateOpenedFirst,
      curTimeFormat: timeFormat
    } = this.state;
    newValue = this.getEndDateByDuration(newValue);
    const editState = endDateOpenedFirst ? 'start' : 'end';
    const date = this.filterDate(newValue, {
      type: 'end',
      originValue: endDate,
      timeFormat,
      autoInitDefaultValue: !!timeFormat && newValue && !endDate
    });
    this.setState(
      {
        endDate: date,
        endInputValue: date.format(inputFormat)
      },
      () => {
        (embed || isMobile()) && this.confirm(isHandleChange);
      }
    );

    if (type !== 'input-datetime-range' && !isMobile()) {
      this.setState({ editState });
    }
  }

  // 手动控制输入时间
  startInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { onChange, inputFormat, utc } = this.props;
    const value = e.currentTarget.value;
    this.setState({ startInputValue: value });
    if (value === '') {
      onChange('');
    } else {
      let newDate = this.getStartDateByDuration(
        moment(value, inputFormat)
      );
      this.setState({ startDate: newDate });
    }
  }

  /** 获取宽度类型变量的值 */
  getValidWidthValue = (element: HTMLElement, propsName: string): number => {
    if (!element || !propsName) {
      return 0;
    }
    const propsValue = parseInt(
      getComputedStyle(element, kebabCase(propsName)),
      10
    );

    return isNaN(propsValue) ? 0 : propsValue;
  }

  renderActiveCursor = () => {
    const { classnames: cx } = this.props;
    const { editState, isFocused } = this.state;
    let cursorWidth: number = 0;
    let cursorLeft: number = 0;

    const parentNode = this?.dom?.current;
    const startInputNode = this?.startInputRef?.current;
    const endInputNode = this?.endInputRef?.current;
    const separatorNode = this?.separatorRef?.current;

    if (parentNode && startInputNode && endInputNode && separatorNode) {
      if (editState === 'start') {
        const paddingWidth = this.getValidWidthValue(parentNode, 'paddingLeft');

        cursorLeft = paddingWidth;
        cursorWidth = startInputNode.offsetWidth;
      } else if (editState === 'end') {
        const separatorWidth =
          separatorNode.offsetWidth +
          this.getValidWidthValue(parentNode, 'paddingLeft') +
          this.getValidWidthValue(parentNode, 'marginLeft') +
          this.getValidWidthValue(parentNode, 'paddingRight') +
          this.getValidWidthValue(parentNode, 'marginRight');

        cursorLeft = startInputNode.offsetWidth + separatorWidth;
        cursorWidth = endInputNode.offsetWidth;
      } else {
        cursorWidth = 0;
      }
    }
    return (
      <div
        className={cx('DateRangePicker-activeCursor', { isFocused })}
        style={{
          position: 'absolute',
          left: cursorLeft,
          width: cursorWidth
        }}
      />
    );
  }

  //记录当前时间范围展示的时间，用于控制点击上一年，上一个月等
  viewStartYear?: number = undefined;
  viewStartMonth?: number = undefined;
  viewEndYear?: number = undefined;
  viewEndMonth?: number = undefined;
  changeCurrentShowTime = (isStart: boolean, isYear: boolean, value: number) => {
    if (isStart) {
      isYear ? this.viewStartYear = value : this.viewStartMonth = value
    } else {
      isYear ? this.viewEndYear = value : this.viewEndMonth = value
    }
  }

  renderCalendar = () => {
    const {
      classPrefix: ns,
      classnames: cx,
      dateFormat,
      timeFormat,
      inputFormat,
      ranges,
      locale,
      embed,
      viewMode = 'days',
      type
    } = this.props;
    const __ = this.props.translate;
    const mobileUI = isMobile();
    const { startDate, endDate, editState } = this.state;
    const isDateTimeRange = type === 'input-datetime-range';
    // timeRange需要单独选择范围
    const isTimeRange = isDateTimeRange || viewMode === 'time';
    const isDateRange = type === 'input-date-range';
    const isConfirmBtnDisbaled =
      (isTimeRange && editState === 'start' && !startDate) ||
      (isTimeRange && editState === 'end' && !endDate) ||
      (startDate && endDate?.isBefore?.(this.state.startDate)) ||
      /** 日期范围选择之后会立即切换面板，所以开始/结束日期任意一个不合法就不允许更新数据 */
      (isDateRange &&
        (!startDate ||
          !endDate ||
          !startDate?.isValid() ||
          !endDate?.isValid()));
    return (
      <div className={cx(`${ns}DateRangePicker-wrap`, { 'is-mobile': isMobile() })} ref={this.calendarRef}>
        <div className='dateRange-body'>
          {this.renderRanges(ranges)}
          <div className='dateRangePicker'>
            <div
              className={cx(`${ns}DateRangePicker-picker-wrap`, {
                'is-vertical': embed
              })}
            >
              {(!isTimeRange ||
                (editState === 'start' && !embed) ||
                (mobileUI && isTimeRange)) && (
                  <Calendar
                    className={`${ns}DateRangePicker-start`}
                    value={startDate}
                    // 区分的原因是 time-range 左侧就只能选起始时间，而其它都能在左侧同时同时选择起始和结束
                    // TODO: 后续得把 time-range 代码拆分出来
                    onChange={
                      isDateTimeRange
                        ? this.handleStartDateChange
                        : viewMode === 'time'
                          ? this.handleTimeStartChange
                          : this.handleDateChange
                    }
                    requiredConfirm={false}
                    dateFormat={dateFormat}
                    inputFormat={inputFormat}
                    timeFormat={timeFormat}
                    isValidDate={this.checkStartIsValidDate}
                    viewMode={viewMode}
                    input={false}
                    onClose={this.close}
                    renderDay={this.renderDay}
                    renderQuarter={this.renderQuarter}
                    locale={locale}
                    translate={__}
                    initial={this.props.initial}
                    timeRangeHeader={__('DateRange.startTime')}
                    viewStartYear={this.viewStartYear}
                    viewEndYear={this.viewEndYear}
                    viewStartMonth={this.viewStartMonth}
                    viewEndMonth={this.viewEndMonth}
                    changeCurrentShowTime={this.changeCurrentShowTime}
                  />
                )}
              {(!isTimeRange ||
                (editState === 'end' && !embed) ||
                (mobileUI && isTimeRange)) && (
                  <Calendar
                    className={`${ns}DateRangePicker-end`}
                    value={endDate}
                    onChange={
                      isDateTimeRange
                        ? this.handelEndDateChange
                        : viewMode === 'time'
                          ? this.handleTimeEndChange
                          : this.handleDateChange
                    }
                    requiredConfirm={false}
                    dateFormat={dateFormat}
                    inputFormat={inputFormat}
                    timeFormat={timeFormat}
                    viewDate={this.state.endDate ? this.state.endDate : isDateTimeRange ? this.currentMonth : this.nextMonth}
                    isEndDate
                    isValidDate={this.checkEndIsValidDate}
                    viewMode={viewMode}
                    input={false}
                    onClose={this.close}
                    renderDay={this.renderDay}
                    renderQuarter={this.renderQuarter}
                    locale={locale}
                    translate={__}
                    initial={this.props.initial}
                    timeRangeHeader={__('DateRange.endTime')}
                    viewStartYear={this.viewStartYear}
                    viewEndYear={this.viewEndYear}
                    viewStartMonth={this.viewStartMonth}
                    viewEndMonth={this.viewEndMonth}
                    changeCurrentShowTime={this.changeCurrentShowTime}
                  />
                )}
            </div>
            {embed || mobileUI ? null : (
              <div key="button" className={`${ns}DateRangePicker-actions`}>
                <Button size="sm" onClick={() => this.close(true)}>
                  {__('cancel')}
                </Button>
                <Button
                  level="primary"
                  size="sm"
                  className={cx('m-l-sm')}
                  disabled={isConfirmBtnDisbaled}
                  onClick={() => this.confirm()}
                >
                  {__('confirm')}
                </Button>
              </div>
            )}
          </div>
        </div>
      </div>
    );
  }

  handleTimeClick = (status: 'start' | 'end') => {
    const { editState } = this.state;
    if (editState === status) return;
    this.setState({ editState: status });
  }

  showTimes = () => {
    const { translate: __ } = this.props;
    const { startInputValue, endInputValue, editState } = this.state;
    return (
      <div className='selected-date-show-box'>
        <div
          onClick={() => this.handleTimeClick('start')}
          className={`show-start show-date-item ${editState === 'start' ? 'actived' : ''} ${startInputValue ? '' : 'empty'}`}>{startInputValue || __('DateRange.startTime')}</div>
        <div
          onClick={() => this.handleTimeClick('end')}
          className={`show-end show-date-item ${editState === 'end' ? 'actived' : ''} ${endInputValue ? '' : 'empty'}`}>{endInputValue || __('DateRange.endTime')}</div>
      </div>
    )
  }

  renderMobileRange = () => {
    const { timeFormat, inputFormat, dateFormat, ranges, viewMode, type, locale, translate: __ } = this.props;
    const { startDate, endDate, editState } = this.state;
    const isDateTimeRange = type === 'input-datetime-range';
    return <>
      {this.renderRanges(ranges)}
      {this.showTimes()}
      <div key={editState}>
        {editState === 'start' ? <Calendar
          value={startDate}
          // 区分的原因是 time-range 左侧就只能选起始时间，而其它都能在左侧同时同时选择起始和结束
          // TODO: 后续得把 time-range 代码拆分出来
          onChange={
            isDateTimeRange
              ? this.handleStartDateChange
              : viewMode === 'time'
                ? this.handleTimeStartChange
                : this.handleDateChange
          }
          requiredConfirm={!!(dateFormat && timeFormat)}
          dateFormat={dateFormat}
          inputFormat={inputFormat}
          timeFormat={timeFormat}
          isValidDate={this.checkStartIsValidDate}
          viewMode={viewMode}
          input={false}
          onClose={this.close}
          locale={locale}
          translate={__}
          useMobileUI
          initial={this.props.initial}
          type={type}
        /> :
          <Calendar
            value={endDate}
            onChange={
              isDateTimeRange
                ? this.handelEndDateChange
                : viewMode === 'time'
                  ? this.handleTimeEndChange
                  : this.handleDateChange
            }
            requiredConfirm={false}
            dateFormat={dateFormat}
            inputFormat={inputFormat}
            timeFormat={timeFormat}
            viewDate={this.state.endDate ? this.state.endDate : this.nextMonth}
            isEndDate
            isValidDate={this.checkEndIsValidDate}
            viewMode={viewMode}
            input={false}
            onClose={this.close}
            renderDay={this.renderDay}
            renderQuarter={this.renderQuarter}
            locale={locale}
            translate={__}
            useMobileUI
            initial={this.props.initial}
            type={type}
          />
        }
      </div>
    </>
  }

  getTarget() {
    return this.dom;
  }

  render() {
    const {
      className,
      popoverClassName,
      classPrefix: ns,
      value,
      popOverContainer,
      inputFormat,
      format,
      joinValues,
      delimiter,
      clearable,
      disabled,
      embed,
      overlayPlacement,
      borderMode,
      useMobileUI,
      viewMode = 'days',
      animation = true,
      classnames: cx
    } = this.props;
    const useCalendarMobile =
      useMobileUI &&
      isMobile() &&
      ['days', 'months', 'quarters', 'time', 'years'].indexOf(viewMode) > -1;

    const { isOpened, isFocused, startDate, endDate } = this.state;

    const selectedDate = DateRangePicker.unFormatValue(
      value,
      format,
      joinValues,
      delimiter
    );
    const startViewValue = selectedDate.startDate
      ? selectedDate.startDate.format(inputFormat)
      : '';
    const endViewValue = selectedDate.endDate
      ? selectedDate.endDate.format(inputFormat)
      : '';
    const arr = [];
    startViewValue && arr.push(startViewValue);
    endViewValue && arr.push(endViewValue);
    const __ = this.props.translate;

    if (embed) {
      return (
        <div
          className={cx(
            `${ns}DateRangeCalendar`,
            {
              'is-disabled': disabled
            },
            className
          )}
        >
          {useCalendarMobile ? this.renderMobileRange() : this.renderCalendar()}
        </div>
      );
    }
    /** 是否启用游标动画 */
    const useAnimation = animation !== false;

    return (
      <div
        tabIndex={0}
        onFocus={this.handleFocus}
        onBlur={this.handleBlur}
        className={cx(
          `${ns}DateRangePicker`,
          {
            'is-disabled': disabled,
            'is-focused': isFocused,
            [`${ns}DateRangePicker--border${ucFirst(borderMode)}`]: borderMode,
            'is-mobile': useMobileUI && isMobile()
          },
          className
        )}
        ref={this.dom}
        onClick={this.handleClick}
      >
        {/* Aug 添加一层div 防止内容溢出*/}
        <div className={cx(`${ns}DateRangePicker-content`)}>
          {
            isMobile() ? <div className='show-datetime-value'>
              <div className='show-datetime-value-item start'>
                <Input
                  className={cx(`${ns}DateRangePicker-input`, {
                    isActive: !useAnimation && this.state.editState === 'start' && isOpened
                  })}
                  onChange={(e) => {
                    this.startInputChange(e)
                  }}
                  onClick={this.openStart}
                  ref={this.startInputRef}
                  placeholder={__('DateRange.startTime')}
                  autoComplete="off"
                  value={this.state.startInputValue || ''}
                  disabled={disabled}
                  readOnly={isMobile()}
                />
                <span
                  className={cx(`${ns}DateRangePicker-input-separator`)}
                  ref={this.separatorRef}
                  onClick={this.openStart}
                >
                  <span className={cx(`${ns}DateRangePicker-input-separator-line`)}></span>
                </span>
                {clearable && !disabled && value ? (
                  <a className={`${ns}DateRangePicker-clear`} onClick={this.clearValue}>
                    <Icon icon="close" className="icon" />
                  </a>
                ) : null}

                <a className={cx(`${ns}DateRangePicker-toggler`)} onClick={this.openStart}>
                  {isMobile() ? <Icon icon="right-arrow-bold" className="icon" /> : <Icon
                    icon='clock'
                    className="icon"
                  />}
                </a>
              </div>
              <div className='show-datetime-value-item'>
                <Input
                  className={cx(`${ns}DateRangePicker-input`, {
                    isActive: !useAnimation && this.state.editState === 'end' && isOpened
                  })}
                  onChange={this.endInputChange}
                  onClick={this.openStart}
                  ref={this.endInputRef}
                  placeholder={__('DateRange.endTime')}
                  autoComplete="off"
                  value={this.state.endInputValue || ''}
                  disabled={disabled}
                  readOnly={isMobile()}
                />
              </div>
            </div> : <>
              <Input
                className={cx(`${ns}DateRangePicker-input`, {
                  isActive: !useAnimation && this.state.editState === 'start' && isOpened
                })}
                onChange={(e) => {
                  this.startInputChange(e)
                }}
                onClick={this.openStart}
                ref={this.startInputRef}
                placeholder={__('DateRange.startTime')}
                autoComplete="off"
                value={this.state.startInputValue || ''}
                disabled={disabled}
                readOnly={isMobile()}
              />
              <span
                className={cx(`${ns}DateRangePicker-input-separator`)}
                ref={this.separatorRef}
                onClick={this.openStart}
              >
                <span className={cx(`${ns}DateRangePicker-input-separator-line`)}></span>
              </span>
              <Input
                className={cx(`${ns}DateRangePicker-input`, {
                  isActive: !useAnimation && this.state.editState === 'end' && isOpened
                })}
                onChange={this.endInputChange}
                onClick={this.openEnd}
                ref={this.endInputRef}
                placeholder={__('DateRange.endTime')}
                autoComplete="off"
                value={this.state.endInputValue || ''}
                disabled={disabled}
                readOnly={isMobile()}
              />

              {/* 指示游标 */}
              {useAnimation && !isMobile() ? this.renderActiveCursor() : null}

              {clearable && !disabled && value ? (
                <a className={`${ns}DateRangePicker-clear`} onClick={this.clearValue}>
                  <Icon icon="close" className="icon" />
                </a>
              ) : null}

              <a className={cx(`${ns}DateRangePicker-toggler`)} onClick={this.openStart}>
                {isMobile() ? <Icon icon="right-arrow-bold" className="icon" /> : <Icon
                  icon='clock'
                  className="icon"
                />}
              </a>
            </>
          }
        </div>

        {isOpened ? (
          useMobileUI && isMobile() ? (
            <PopUp
              isShow={isOpened}
              container={popOverContainer ?? document.getElementById('amis-modal-container')!}
              className={cx(`${ns}DatePicker-popup DatePicker-mobile`)}
              onHide={this.close}
              showClose
              header={<div className={cx('TransferDropDown-header')}>{this.props?.label ?? this.props?.domicile?.label ?? ''}</div>}
              onActonClick={() => {
                this.props.onChange?.('')
                this.setState({
                  startDate: undefined,
                  endDate: undefined,
                  startInputValue: '',
                  endInputValue: ''
                })
              }}
            >
              {useCalendarMobile ? this.renderMobileRange() : this.renderCalendar()}
            </PopUp>
          ) : (
            <Overlay
              target={this.dom.current}
              onHide={this.close}
              container={(() => findDOMNode(this))}
              rootClose={false}
              placement={overlayPlacement}
              show
            >
              <PopOver
                classPrefix={ns}
                className={cx(`${ns}DateRangePicker-popover`, popoverClassName)}
                onHide={this.close}
                onClick={this.handlePopOverClick}
              >
                {this.renderCalendar()}
              </PopOver>
            </Overlay>
          )
        ) : null
        }
      </div>
    );
  }
}

export default themeable(localeable(DateRangePicker));
