# FormDatepicker

> 日期选择器可以选择范围日期，单个日期，选择时间，以及渲染成其他任何组件

## 日期值的获取和格式化

### value

`value` 是日期选择器选择的时间，给 `date-range` 传值可以初始化日期选择器的初始值。

```html
<nly-form-daterangepicker
  :value='{
          startDate: "2019-12-10",
          endDate: "2019-12-20"
        }'
/>

<!-- value.name -->
<!-- date-picker.vue -->
```

`value` 是可以双向绑定数据的，即使用 `v-model` 指令来绑定, 这样可以设定日期选择器的初始化，也能获取到他的修改值

```html
<template>
  <div>
    <nly-form-daterangepicker v-model="dateRange" />
    <nly-form-group label="起始时间">
      <nly-form-input v-model="dateRange.startDate" type="text" />
    </nly-form-group>
    <nly-form-group label="结束时间">
      <nly-form-input v-model="dateRange.endDate" type="text" />
    </nly-form-group>
  </div>
</template>
<script>
  export default {
    data() {
      return {
        dateRange: {
          startDate: "2019-12-10",
          endDate: "2019-12-20"
        }
      };
    }
  };
</script>

<!-- v-model value.name -->
<!-- date-picker.vue -->
```

**注意**

- 默认渲染下， 不支持从日期选择器中输入日期来调整日历上的时间

### locale-data 格式化默认显示时间

可以使用 `locale-data` 来设置输入框显示的数据格式，比如设置 `:locale-data = "{ firstDay: 1, format: 'yyyy-mm-dd HH:MM:ss' }"` 来使数据显示格式为 2020-01-01 08：30：22

`value` 是日期选择器选择的时间，给 `value` 传值可以初始化日期选择器的初始值。

```html
<nly-form-daterangepicker
  :value='{
          startDate: "2019-12-10",
          endDate: "2019-12-20"
        }'
  :locale-data="{ firstDay: 1, format: 'yyyy-mm-dd HH:MM:ss' }"
/>

<!-- locale-data.name -->
<!-- date-picker.vue -->
```

`locale-data` 可以用来设置一些自定义配置，比如设置中文。

**注意**

- 这里有个 bug，如果星期的名称太长，比如 星期一 这样，会导致日历挤出 dropdown 的包裹，建议星期名称只设置一个字

- 这个 bug 暂时不会修复

```html
<template>
  <nly-form-daterangepicker
    :value='{
          startDate: "2019-12-10",
          endDate: "2019-12-20"
        }'
    :locale-data="localeData"
    show-week-numbers
    show-dropdowns
    :ranges="ranges"
  />
</template>

<script>
  export default {
    data() {
      return {
        localeData: {
          direction: "ltr",
          format: "mm/dd/yyyy",
          separator: " - ",
          applyLabel: "确定",
          cancelLabel: "取消",
          weekLabel: "周",
          customRangeLabel: "Custom Range",
          daysOfWeek: ["日", "一", "二", "三", "四", "五", "六"],
          monthNames: [
            "1月",
            "2月",
            "3月",
            "4月",
            "5月",
            "6月",
            "7月",
            "8月",
            "9月",
            "10月",
            "11月",
            "12月"
          ],
          firstDay: 0
        },
        ranges: false
      };
    },
    mounted() {
      let today = new Date();
      today.setHours(0, 0, 0, 0);

      let yesterday = new Date();
      yesterday.setDate(today.getDate() - 1);
      yesterday.setHours(0, 0, 0, 0);
      let thisMonthStart = new Date(today.getFullYear(), today.getMonth(), 1);
      let thisMonthEnd = new Date(today.getFullYear(), today.getMonth() + 1, 0);
      this.ranges = {
        今天: [today, today],
        昨天: [yesterday, yesterday],
        本月: [thisMonthStart, thisMonthEnd],
        今年: [
          new Date(today.getFullYear(), 0, 1),
          new Date(today.getFullYear(), 11, 31)
        ],
        上个月: [
          new Date(today.getFullYear(), today.getMonth() - 1, 1),
          new Date(today.getFullYear(), today.getMonth(), 0)
        ]
      };
    }
  };
</script>
<!-- locale-data.name -->
<!-- date-picker.vue -->
```

### update 获取选择时间

不使用 `v-model` 指令，也可以通过 update 函数来获取日期选择器选择的时间, 默认获取的时间是 GMT 时间， 格式需要自己定义函数来修改

```html
<template>
  <div>
    <nly-form-daterangepicker
      :value='{
          startDate: "2019-12-10",
          endDate: "2019-12-20"
        }'
      :locale-data="{ firstDay: 1, format: 'yyyy-mm-dd HH:MM:ss' }"
      @update="update"
    />
    <p>
      <span>起始时间： {{startDate}}</span>
    </p>
    <p>
      <span>结束时间： {{endDate}}</span>
    </p>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        startDate: null,
        endDate: null
      };
    },
    methods: {
      update(value) {
        this.startDate = this.gmtFormat(value.startDate);
        this.endDate = this.gmtFormat(value.endDate);
      },
      gmtFormat(time) {
        let date = new Date(time);
        let Str =
          date.getFullYear() +
          "-" +
          (date.getMonth() + 1) +
          "-" +
          date.getDate() +
          " " +
          date.getHours() +
          ":" +
          date.getMinutes() +
          ":" +
          date.getSeconds();
        return Str;
      }
    }
  };
</script>

<!-- locale-data.name -->
<!-- date-picker.vue -->
```

### 最小和最大时间限制

设置 `min-date` 和 `max-date` prop 可以限制日期选择器的 时间选择范围，超出这个范围的时间是无法选择的，且日历上也无法联动现实超出这个范围的时间

```html
<template>
  <div>
    <nly-form-daterangepicker
      :locale-data="{ firstDay: 1, format: 'yyyy-mm-dd HH:MM:ss' }"
      :min-date="minDate"
      :max-date="maxDate"
      v-model="dateRange"
      show-dropdowns
    />

    <nly-form-group label="起始时间">
      <nly-form-input v-model="dateRange.startDate" type="text" />
    </nly-form-group>
    <nly-form-group label="结束时间">
      <nly-form-input v-model="dateRange.endDate" type="text" />
    </nly-form-group>
  </div>
</template>
<script>
  export default {
    data() {
      return {
        dateRange: {
          startDate: "2019-12-10",
          endDate: "2019-12-20"
        },
        minDate: "2019-5-1 04:00:00",
        maxDate: "2021-1-1 04:00:00"
      };
    }
  };
</script>

<!-- min & max date.name -->
<!-- date-picker.vue -->
```

## 渲染输入框的一些设置

### prepend 和 append

`prepend` 和 `append` prop 可以设置前后显示文字，渲染成类似 icon 的效果

```html
<template>
  <div>
    <nly-form-daterangepicker
      :value='{
          startDate: "2019-12-10",
          endDate: "2019-12-20"
        }'
      :locale-data="{ firstDay: 1, format: 'yyyy-mm-dd HH:MM:ss' }"
      :prepend="prepend"
      :append="append"
    />
    <nly-form-group
      label="prepend 输入框前面图标"
      label-cols-xs="auto"
      class="mt-2"
    >
      <nly-form-input v-model="prepend" size="sm" />
    </nly-form-group>

    <nly-form-group label="append 输入框前面图标" label-cols-xs="auto">
      <nly-form-input v-model="append" size="sm" />
    </nly-form-group>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        prepend: "",
        append: ""
      };
    }
  };
</script>

<!-- prepend and append .name -->
<!-- date-picker.vue -->
```

也可以使用 `prepend-html` 和 `append-html` prop 来设置输入框前后显示的内容。由于是 html 字符串，请谨慎使用

```html
<template>
  <nly-form-daterangepicker
    :value='{
          startDate: "2019-12-10",
          endDate: "2019-12-20"
        }'
    :locale-data="{ firstDay: 1, format: 'yyyy-mm-dd HH:MM:ss' }"
    :prepend-html="prepend"
    :append-html="append"
  />
</template>

<script>
  export default {
    data() {
      return {
        prepend: "<b>w</b>",
        append: "<b>Q</b>"
      };
    }
  };
</script>

<!-- prepend-html and append-html .name -->
<!-- date-picker.vue -->
```

还可以使用 `prepend` 和 `append` 插槽插入其他嵌套元素

```html
<template>
  <nly-form-daterangepicker
    :value='{
          startDate: "2019-12-10",
          endDate: "2019-12-20"
        }'
    :locale-data="{ firstDay: 1, format: 'yyyy-mm-dd HH:MM:ss' }"
  >
    <template v-slot:prepend>
      <nly-input-group-text>
        <nly-icon icon="nlyfont nly-icon-time" />
      </nly-input-group-text>
    </template>
    <template v-slot:append>
      <nly-dropdown text="Dropdown" variant="success">
        <nly-dropdown-item>Action A</nly-dropdown-item>
        <nly-dropdown-item>Action B</nly-dropdown-item>
      </nly-dropdown>
    </template>
  </nly-form-daterangepicker>
</template>

<!-- prepend-html and append-html .name -->
<!-- date-picker.vue -->
```

### 控制大小

`nly-form-daterangepicker` 提供一个 `size` prop 来控制默认渲染的输入框大小， 可选 `sm`, `md`, `lg`。

```html
<template>
  <div>
    <nly-form-daterangepicker
      :value='{
          startDate: "2019-12-10",
          endDate: "2019-12-20"
        }'
      :locale-data="{ firstDay: 1, format: 'yyyy-mm-dd HH:MM:ss' }"
      size="sm"
    >
    </nly-form-daterangepicker>
    <nly-form-daterangepicker
      :value='{
          startDate: "2019-12-10",
          endDate: "2019-12-20"
        }'
      :locale-data="{ firstDay: 1, format: 'yyyy-mm-dd HH:MM:ss' }"
      size="md"
      class="mt-2"
    >
    </nly-form-daterangepicker>
    <nly-form-daterangepicker
      :value='{
          startDate: "2019-12-10",
          endDate: "2019-12-20"
        }'
      :locale-data="{ firstDay: 1, format: 'yyyy-mm-dd HH:MM:ss' }"
      size="lg"
      class="mt-2"
    >
    </nly-form-daterangepicker>
  </div>
</template>

<!-- prepend-html and append-html .name -->
<!-- date-picker.vue -->
```

### valid 验证提示

`nly-form-daterangepicker` 提供一个 `valid` 和 `invalid-feedback` , `valid-feedback`, `waring-feedback` 来显示验证信息

```html
<template>
  <div>
    <nly-form-daterangepicker
      :value='{
          startDate: "2019-12-10",
          endDate: "2019-12-20"
        }'
      :locale-data="{ firstDay: 1, format: 'yyyy-mm-dd HH:MM:ss' }"
      prepend="@"
      append="!"
      invalid-feedback="invalid"
      valid-feedback="valid"
      warning-feedback="warning"
      :valid="valid"
    />
    <nly-form-group label="valid 表单状态" label-cols-xs="auto" class="mt-2">
      <nly-form-select v-model="valid" :options="validOptinos" />
    </nly-form-group>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        valid: "novalid",
        validOptinos: [
          { value: null, text: "Please select an option", disabled: true },
          { value: "novalid", text: "不显示 feedback" },
          { value: "invalid", text: "显示invalid feedback" },
          { value: "valid", text: "显示 valid feedback" },
          { value: "warning", text: "显示 warning feedback" }
        ]
      };
    }
  };
</script>

<!-- valid .name -->
<!-- date-picker.vue -->
```

## 日历的一些设置

- 使用 `opens` prop 来设置日历的位置, 可选 `center`, `left`, `right`, `inline`

- 使用 `single-date-picker` prop 来选择日期选择器的选择模式，分为单个日期选择和范围日期选择，设置 `single-date-picker` prop 为 `true` 为范围日期选择， `false` 为 单个日期选择

- 设置 `show-week-numbers` prop 为 `true` 来显示日历上的周数，设置为 `false` 不显示

- 设置 `time-picker` prop 为 `true` 来显示时间选择器

- 设置 `time-picker24-hour` prop 为 `true` 来显示 24 小时制时间显示器，必须设置 `time-picker` prop 为 `true` 才有效

- 设置 `show-dropdowns` prop 为 `true` 来显示月份选择下拉框和年份快速输入

- 设置 `auto-apply` prop 为 `true` 来设置自动提交选择的时间

- 设置 `ranges` prop 为 `true` 来显示左侧快速选择时间

- 设置 `always-show-calendars` prop 为 `true` 来显示日历

- 设置 `append-to-body` prop 为 `true` 来把日历渲染到 body 尾部，不然渲染在输入框下面, **注意 `append-to-body` 为 `false` 时可能会影响布局，如果影响布局， 请设置 `append-to-body` 为 `true`**

- 设置 `close-on-esc` prop 为 `true` 来支持键盘按键 <kbd>ESC</kbd> 退出日历

- 设置 `linked-calendars` prop 为 `true` 来使得两个日历联动

```html
<template>
  <div>
    <nly-form-daterangepicker
      :opens="opens"
      :locale-data="{ firstDay: 1, format: 'yyyy-mm-dd HH:MM:ss' }"
      :single-date-picker="singleDatePicker"
      :time-picker="timePicker"
      :time-picker24-hour="timePicker24Hour"
      :show-week-numbers="showWeekNumbers"
      :show-dropdowns="showDropdowns"
      :auto-apply="autoApply"
      v-model="dateRange"
      :ranges="show_ranges ? undefined : false"
      :linked-calendars="linkedCalendars"
      :always-show--calendars="alwaysShowCalendars"
      :append-to-body="appendToBody"
      :close-on-esc="closeOnEsc"
    />
    <div>
      日历布局
      <div class="form-check form-check-inline">
        <input
          class="form-check-input"
          type="radio"
          name="options"
          id="option1"
          value="left"
          v-model="opens"
        />
        <label class="form-check-label">left 左侧</label>
      </div>
      <div class="form-check form-check-inline">
        <input
          class="form-check-input"
          type="radio"
          name="options"
          id="option2"
          value="center"
          v-model="opens"
        />
        <label class="form-check-label">center 居中</label>
      </div>
      <div class="form-check form-check-inline">
        <input
          class="form-check-input"
          type="radio"
          name="options"
          id="option3"
          value="right"
          v-model="opens"
        />
        <label class="form-check-label">right 右侧</label>
      </div>
      <div class="form-check form-check-inline">
        <input
          class="form-check-input"
          type="radio"
          name="options"
          id="option4"
          value="inline"
          v-model="opens"
        />
        <label class="form-check-label">inline 垂直</label>
      </div>
      <small class="form-text text-muted"
        >日历布局，可选左侧，居中，右侧，垂直，当为垂直的时候，无法关闭
      </small>
    </div>
    <div>
      <div class="form-check form-inline py-3">
        <label class="form-check-label" for="singleDatePicker">
          single-date-picker 日历选择模式
        </label>
        <select
          v-model="singleDatePicker"
          id="singleDatePicker"
          class="form-control ml-3"
        >
          <option :value="true">single 单个日期</option>
          <option :value="false">range 范围日期</option>
          <option :value="false">默认</option>
        </select>
        <small class="form-text text-muted"
          >单个日期只能选择一个日期，范围日期必须选择两个日期</small
        >
      </div>
    </div>
    <div class="form-check">
      <input
        class="form-check-input"
        type="checkbox"
        id="showWeekNumbers"
        v-model="showWeekNumbers"
      />
      <label class="form-check-label" for="showWeekNumbers">
        show-week-numbers 显示周数
      </label>
      <small class="form-text text-muted">
        日历左侧显示整个日期对应的周数
      </small>
    </div>
    <div class="form-check">
      <input
        class="form-check-input"
        type="checkbox"
        id="timePicker"
        v-model="timePicker"
      />
      <label class="form-check-label" for="timePicker">
        time-picker 显示时间选择器
      </label>
      <small class="form-text text-muted">
        允许选择时间
      </small>
    </div>
    <div class="form-check">
      <input
        class="form-check-input"
        type="checkbox"
        id="timePicker24Hour"
        v-model="timePicker24Hour"
      />
      <label class="form-check-label" for="timePicker24Hour">
        time-picker24-hour 24小时制时间选择器
      </label>
      <small class="form-text text-muted">
        时间显示器转为24小时制
      </small>
    </div>
    <div class="form-check">
      <input
        class="form-check-input"
        type="checkbox"
        id="showDropdowns"
        v-model="showDropdowns"
      />
      <label class="form-check-label" for="showDropdowns">
        show-dropdowns 显示选择月份和输入年份
      </label>
      <small class="form-text text-muted">
        允许 显示选择月份和输入年份
      </small>
    </div>
    <div class="form-check">
      <input
        class="form-check-input"
        type="checkbox"
        id="autoApply"
        v-model="autoApply"
      />
      <label class="form-check-label" for="autoApply">
        auto-apply 自动提交
      </label>
      <small class="form-text text-muted">
        选择完日期之后，会自动提交选择的时间，并隐藏停止，提交按钮
      </small>
    </div>
    <div class="form-check">
      <input
        class="form-check-input"
        type="checkbox"
        id="show_ranges"
        v-model="show_ranges"
      />
      <label class="form-check-label" for="show_ranges">
        ranges 显示左侧最近时间
      </label>
      <small class="form-text text-muted">
        可以显示左侧最近时间，比如上个月
      </small>
    </div>
    <div class="form-check">
      <input
        class="form-check-input"
        type="checkbox"
        id="alwaysShowCalendars"
        v-model="alwaysShowCalendars"
      />
      <label class="form-check-label" for="alwaysShowCalendars">
        always-show-calendars 显示日历
      </label>
      <small class="form-text text-muted">
        设置为false 不显示日历，如果设置了rangs显示左侧最近时间，这个选项无效
      </small>
    </div>
    <div class="form-check">
      <input
        class="form-check-input"
        type="checkbox"
        id="appendToBody"
        v-model="appendToBody"
      />
      <label class="form-check-label" for="appendToBody">
        append-to-body 把日期选择器渲染到 body
      </label>
      <small class="form-text text-muted"> </small>
    </div>
    <div class="form-check">
      <input
        class="form-check-input"
        type="checkbox"
        id="closeOnEsc"
        v-model="closeOnEsc"
      />
      <label class="form-check-label" for="closeOnEsc">
        close-on-esc 按esc键退出
      </label>
    </div>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        dateRange: {
          startDate: "2019-12-10",
          endDate: "2019-12-20"
        },
        single_range_picker: false,
        show_ranges: true,
        singleDatePicker: false,
        timePicker: true,
        timePicker24Hour: true,
        showDropdowns: true,
        autoApply: false,
        showWeekNumbers: true,
        linkedCalendars: true,
        alwaysShowCalendars: true,
        appendToBody: false,
        closeOnEsc: true,
        opens: "center"
      };
    }
  };
</script>

<!-- calendars .name -->
<!-- date-picker.vue -->
```

## 禁用日期选择器

使用 `disabled` prop 可以禁用日历选择时间，这时候点击并不会显示日期选择器

```html
<nly-form-daterangepicker
  :value='{
          startDate: "2019-12-10",
          endDate: "2019-12-20"
        }'
  :locale-data="{ firstDay: 1, format: 'yyyy-mm-dd HH:MM:ss' }"
  disabled
/>

<!-- disabled.name -->
<!-- date-picker.vue -->
```

可以使用 `date-format` prop 接受有个函数来设置禁止选择日期。给 `date-format` 函数的第一个参数 classes 添加一个 disabled 属性

```html
<template>
  <nly-form-daterangepicker
    :value='{
          startDate: "2019-12-10",
          endDate: "2019-12-20"
        }'
    :locale-data="{ firstDay: 1, format: 'yyyy-mm-dd HH:MM:ss' }"
    :date-format="dateFormat"
  />
</template>

<script>
  export default {
    methods: {
      dateFormat(classes, date) {
        if (!classes.disabled) {
          classes.disabled = date.getTime() < new Date();
        }
        return classes;
      }
    }
  };
</script>

<!-- dateFormat disabled.name -->
<!-- date-picker.vue -->
```

## 使用插槽

### input 插槽

我们可以使用 input 插槽来使默认渲染的 input 输入框变成其他想渲染的任何组件。

**注意**

- 如果影响布局， 请设置 `append-to-body` 为 `true`

- 需要给渲染的组件绑定 `click` 事件为 `clickPicker`

```html
<template>
  <div>
    <nly-form-daterangepicker
      :value='{
          startDate: "2019-12-10",
          endDate: "2019-12-20"
        }'
      :append-to-body="true"
    >
      <template #input="picker" style="min-width: 350px;">
        <nly-form-input
          :value='picker.startDate + "-" +  picker.endDate'
          @click="picker.clickPicker"
        />
      </template>
    </nly-form-daterangepicker>

    <nly-input-group>
      <nly-input :value="inputDate" />
      <template v-slot:append>
        <nly-input-group-text>
          <nly-form-daterangepicker v-model="dateRange" :append-to-body="true">
            <template #input="picker">
              <nly-icon
                icon="far fa-calendar-alt"
                @click="picker.clickPicker"
              />
            </template>
          </nly-form-daterangepicker>
        </nly-input-group-text>
      </template>
    </nly-input-group>
  </div>
</template>
<script>
  export default {
    data() {
      return {
        dateRange: {
          startDate: "2019-12-10",
          endDate: "2019-12-20"
        }
      };
    },
    computed: {
      inputDate() {
        const locale =
          this.dateRange.startDate + " - " + this.dateRange.endDate;
        return locale;
      }
    }
  };
</script>
<!-- input 插槽.name -->
<!-- date-picker.vue -->
```

### header 插槽

我们可以使用 header 插槽来给日期选择器添加 header

```html
<template>
  <div>
    <nly-form-daterangepicker
      :value='{
          startDate: "2019-12-10",
          endDate: "2019-12-20"
        }'
      :append-to-body="true"
    >
      <div
        slot="header"
        slot-scope="header"
        :style='{"background-color": "#aaa", padding: "0.5rem", color: "white",display: "flex", "align-items": "center","justify-content": "space-between"}'
      >
        <h3>Calendar header</h3>
        <span v-if="header.in_selection"> - in selection</span>
      </div>
    </nly-form-daterangepicker>
  </div>
</template>
<script>
  export default {
    data() {
      return {
        dateRange: {
          startDate: "2019-12-10",
          endDate: "2019-12-20"
        }
      };
    },
    computed: {
      inputDate() {
        const locale =
          this.dateRange.startDate + " - " + this.dateRange.endDate;
        return locale;
      }
    }
  };
</script>
<!-- header 插槽.name -->
<!-- date-picker.vue -->
```

### ranges 插槽

我们可以使用 ranges 插槽来自定义 ranges 的内容

```html
<template>
  <div>
    <nly-form-daterangepicker
      :value='{
          startDate: "2019-12-10",
          endDate: "2019-12-20"
        }'
      :append-to-body="true"
    >
      <template #ranges="ranges">
        <div class="ranges">
          <ul>
            <li
              v-for="(range, name) in ranges.ranges"
              :key="name"
              @click="ranges.clickRange(range)"
            >
              <b>{{name}}</b>
              <small class="text-muted"
                >{{range[0].toDateString()}} -
                {{range[1].toDateString()}}</small
              >
            </li>
          </ul>
        </div>
      </template>
    </nly-form-daterangepicker>
  </div>
</template>
<script>
  export default {
    data() {
      return {
        dateRange: {
          startDate: "2019-12-10",
          endDate: "2019-12-20"
        }
      };
    },
    computed: {
      inputDate() {
        const locale =
          this.dateRange.startDate + " - " + this.dateRange.endDate;
        return locale;
      }
    }
  };
</script>
<!-- ranges 插槽.name -->
<!-- date-picker.vue -->
```

### footer 插槽

我们可以使用 footer 插槽来自定义 foorer 的内容

```html
<template>
  <div>
    <nly-form-daterangepicker
      :value='{
          startDate: "2019-12-10",
          endDate: "2019-12-20"
        }'
      :append-to-body="true"
    >
      <div
        slot="footer"
        slot-scope="data"
        :style='{"background-color": "#aaa", padding: "0.5rem", color: "white",display: "flex", "align-items": "center","justify-content": "space-between"}'
      >
        <div><b class="text-black">Calendar footer</b> {{data.rangeText}}</div>
        <div style="margin-left: auto">
          <a
            @click="data.clickApply"
            v-if="!data.in_selection"
            class="btn btn-primary btn-sm"
            >Choose current</a
          >
        </div>
      </div>
    </nly-form-daterangepicker>
  </div>
</template>
<script>
  export default {
    data() {
      return {
        dateRange: {
          startDate: "2019-12-10",
          endDate: "2019-12-20"
        }
      };
    },
    computed: {
      inputDate() {
        const locale =
          this.dateRange.startDate + " - " + this.dateRange.endDate;
        return locale;
      }
    }
  };
</script>
<!-- footer 插槽.name -->
<!-- date-picker.vue -->
```

## 可以在其他任何地方打开日期选择器

使用 `this.$refs.pickerRefName.togglePicker(true/false)` 可以在其他元素上点击打开日期选择器

## 完整 demo

这是一个完整的 demo ，差不多包括所有 prop 值的切换

```html
<template>
  <nly-container>
    <nly-row>
      <nly-col>
        <nly-form-group
          label="data picker"
          label-size="lg"
          label-class="font-weight-bold pt-0"
          class="mb-0"
        >
          <nly-form-daterangepicker
            ref="picker"
            :opens="opens"
            :locale-data="{ firstDay: 1, format: 'yyyy-mm-dd HH:MM:ss' }"
            :minDate="minDate"
            :maxDate="maxDate"
            :singleDatePicker="singleDatePicker"
            :timePicker="timePicker"
            :timePicker24Hour="timePicker24Hour"
            :showWeekNumbers="showWeekNumbers"
            :showDropdowns="showDropdowns"
            :autoApply="autoApply"
            v-model="dateRange"
            :ranges="show_ranges ? undefined : false"
            @update="updateValues"
            @toggle="checkOpen"
            :dateFormat="setDateFormat"
            :linkedCalendars="linkedCalendars"
            :alwaysShowCalendars="alwaysShowCalendars"
            :append-to-body="appendToBody"
            :closeOnEsc="closeOnEsc"
            :prepend="prepend"
            :append="append"
            :valid="valid"
            invalid-feedback="我是invalid"
            valid-feedback="我是valid"
            warning-feedback="我是warning"
            size="sm"
          />
        </nly-form-group>
      </nly-col>
    </nly-row>
    <div class="form-row pt-3 bg-light">
      <div class="col-md-6">
        <div class="form-group row">
          <label class="col-sm-4 col-form-label" for="startDate"
            >startDate 开始日期</label
          >
          <div class="col-sm-8">
            <input
              type="text"
              class="form-control"
              id="startDate"
              v-model="dateRange.startDate"
            />
          </div>
        </div>
        <div class="form-group row">
          <label class="col-sm-4 col-form-label" for="endDate"
            >endDate 结束日期</label
          >
          <div class="col-sm-8">
            <input
              type="text"
              class="form-control"
              id="endDate"
              v-model="dateRange.endDate"
            />
          </div>
        </div>
      </div>
      <div class="col-md-6">
        <div class="form-group row">
          <label class="col-sm-4 col-form-label" for="minDate"
            >minDate 日历可选择最小日期</label
          >
          <div class="col-sm-8">
            <input
              type="text"
              class="form-control"
              id="minDate"
              v-model="minDate"
            />
          </div>
        </div>
        <div class="form-group row">
          <label class="col-sm-4 col-form-label" for="maxDate"
            >maxDate 日历可选择最大日期</label
          >
          <div class="col-sm-8">
            <input
              type="text"
              class="form-control"
              id="maxDate"
              v-model="maxDate"
            />
          </div>
        </div>
      </div>
    </div>

    <div>
      日历布局
      <div class="form-check form-check-inline">
        <input
          class="form-check-input"
          type="radio"
          name="options"
          id="option1"
          value="left"
          v-model="opens"
        />
        <label class="form-check-label">left 左侧</label>
      </div>
      <div class="form-check form-check-inline">
        <input
          class="form-check-input"
          type="radio"
          name="options"
          id="option2"
          value="center"
          v-model="opens"
        />
        <label class="form-check-label">center 居中</label>
      </div>
      <div class="form-check form-check-inline">
        <input
          class="form-check-input"
          type="radio"
          name="options"
          id="option3"
          value="right"
          v-model="opens"
        />
        <label class="form-check-label">right 右侧</label>
      </div>
      <div class="form-check form-check-inline">
        <input
          class="form-check-input"
          type="radio"
          name="options"
          id="option4"
          value="inline"
          v-model="opens"
        />
        <label class="form-check-label">inline 垂直</label>
      </div>
      <small class="form-text text-muted"
        >日历布局，可选左侧，居中，右侧，垂直，当为垂直的时候，无法关闭
      </small>
    </div>
    <div>
      <div class="form-check form-inline py-3">
        <label class="form-check-label" for="singleDatePicker">
          single-date-picker 日历选择模式
        </label>
        <select
          v-model="singleDatePicker"
          id="singleDatePicker"
          class="form-control ml-3"
        >
          <option>single 单个日期</option>
          <option>range 范围日期</option>
          <option :value="false">默认</option>
        </select>
        <small class="form-text text-muted"
          >单个日期只能选择一个日期，范围日期必须选择两个日期</small
        >
      </div>

      <!-- prepend="@@"
            append="!"
            valid="invalid"
            invalid-feedback="我是invalid"
            valid-feedback="我是valid"
            warning-feedback="我是warning"
            size="sm" -->

      <nly-form-group label="prepend 输入框前面图标" label-cols-xs="auto">
        <nly-form-input v-model="prepend" size="sm" />
      </nly-form-group>

      <nly-form-group label="append 输入框前面图标" label-cols-xs="auto">
        <nly-form-input v-model="append" size="sm" />
      </nly-form-group>

      <nly-form-group label="valid 表单状态" label-cols-xs="auto">
        <nly-form-select v-model="valid" :options="validOptinos" />
      </nly-form-group>

      <div class="form-check">
        <input
          class="form-check-input"
          type="checkbox"
          id="showWeekNumbers"
          v-model="showWeekNumbers"
        />
        <label class="form-check-label" for="showWeekNumbers">
          show-week-numbers 显示周数
        </label>
        <small class="form-text text-muted">
          日历左侧显示整个日期对应的周数
        </small>
      </div>
      <div class="form-check">
        <input
          class="form-check-input"
          type="checkbox"
          id="timePicker"
          v-model="timePicker"
        />
        <label class="form-check-label" for="timePicker">
          time-picker 显示时间选择器
        </label>
        <small class="form-text text-muted">
          允许选择时间
        </small>
      </div>
      <div class="form-check">
        <input
          class="form-check-input"
          type="checkbox"
          id="timePicker24Hour"
          v-model="timePicker24Hour"
        />
        <label class="form-check-label" for="timePicker24Hour">
          time-picker24-hour 24小时制时间选择器
        </label>
        <small class="form-text text-muted">
          时间显示器转为24小时制
        </small>
      </div>
      <div class="form-check">
        <input
          class="form-check-input"
          type="checkbox"
          id="showDropdowns"
          v-model="showDropdowns"
        />
        <label class="form-check-label" for="showDropdowns">
          show-dropdowns 显示选择月份和输入年份
        </label>
        <small class="form-text text-muted">
          允许 显示选择月份和输入年份
        </small>
      </div>
      <div class="form-check">
        <input
          class="form-check-input"
          type="checkbox"
          id="autoApply"
          v-model="autoApply"
        />
        <label class="form-check-label" for="autoApply">
          auto-apply 自动提交
        </label>
        <small class="form-text text-muted">
          选择完日期之后，会自动提交选择的时间，并隐藏停止，提交按钮
        </small>
      </div>
      <div class="form-check">
        <input
          class="form-check-input"
          type="checkbox"
          id="show_ranges"
          v-model="show_ranges"
        />
        <label class="form-check-label" for="show_ranges">
          ranges 显示左侧最近时间
        </label>
        <small class="form-text text-muted">
          可以显示左侧最近时间，比如上个月
        </small>
      </div>
      <div class="form-check">
        <input
          class="form-check-input"
          type="checkbox"
          id="alwaysShowCalendars"
          v-model="alwaysShowCalendars"
        />
        <label class="form-check-label" for="alwaysShowCalendars">
          always-show-calendars 显示日历
        </label>
        <small class="form-text text-muted">
          设置为false 不显示日历，如果设置了rangs显示左侧最近时间，这个选项无效
        </small>
      </div>
      <div class="form-check">
        <input
          class="form-check-input"
          type="checkbox"
          id="appendToBody"
          v-model="appendToBody"
        />
        <label class="form-check-label" for="appendToBody">
          append-to-body 把日期选择器渲染到 body
        </label>
        <small class="form-text text-muted"> </small>
      </div>
      <div class="form-check">
        <input
          class="form-check-input"
          type="checkbox"
          id="closeOnEsc"
          v-model="closeOnEsc"
        />
        <label class="form-check-label" for="closeOnEsc">
          close-on-esc 按esc键退出
        </label>
      </div>
    </div>
  </nly-container>
</template>

<script>
  export default {
    name: "",
    filters: {
      date(value) {
        if (!value) return "";

        let options = { year: "numeric", month: "long", day: "numeric" };
        return Intl.DateTimeFormat("en-US", options).format(value);
      }
    },
    data() {
      //                    :locale-data="{ daysOfWeek: [ 'Нд', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб' ] }"
      return {
        sss: "",
        opens: "center",
        minDate: "2019-05-02 04:00:00",
        maxDate: "2020-12-26 14:00:00",
        // minDate: '',
        // maxDate: '',
        dateRange: {
          startDate: "2019-12-10",
          endDate: "2019-12-20"
        },
        single_range_picker: false,
        show_ranges: true,
        singleDatePicker: false,
        timePicker: true,
        timePicker24Hour: true,
        showDropdowns: true,
        autoApply: false,
        showWeekNumbers: true,
        linkedCalendars: true,
        alwaysShowCalendars: true,
        appendToBody: false,
        closeOnEsc: true,
        dateUtil: undefined,
        dateFormat: undefined,
        prepend: "@",
        append: "!",
        valid: "novalid",
        validOptinos: [
          { value: null, text: "Please select an option", disabled: true },
          { value: "novalid", text: "不显示 feedback" },
          { value: "invalid", text: "显示invalid feedback" },
          { value: "valid", text: "显示 valid feedback" },
          { value: "warning", text: "显示 warning feedback" }
        ]
      };
    },
    mounted() {
      this.dateFormat = (function() {
        var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZWN]|"[^"]*"|'[^']*'/g;
        var timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g;
        var timezoneClip = /[^-+\dA-Z]/g;

        // Regexes and supporting functions are cached through closure
        return function(date, mask, utc, gmt) {
          // You can't provide utc if you skip other args (use the 'UTC:' mask prefix)
          if (
            arguments.length === 1 &&
            this.kindOf(date) === "string" &&
            !/\d/.test(date)
          ) {
            mask = date;
            date = undefined;
          }

          date = date || new Date();

          if (!(date instanceof Date)) {
            date = new Date(date);
          }

          if (isNaN(date)) {
            throw TypeError("Invalid date");
          }

          mask = String(
            this.dateFormat.masks[mask] ||
              mask ||
              this.dateFormat.masks["default"]
          );

          // Allow setting the utc/gmt argument via the mask
          var maskSlice = mask.slice(0, 4);
          if (maskSlice === "UTC:" || maskSlice === "GMT:") {
            mask = mask.slice(4);
            utc = true;
            if (maskSlice === "GMT:") {
              gmt = true;
            }
          }

          var _ = utc ? "getUTC" : "get";
          var d = date[_ + "Date"]();
          var D = date[_ + "Day"]();
          var m = date[_ + "Month"]();
          var y = date[_ + "FullYear"]();
          var H = date[_ + "Hours"]();
          var M = date[_ + "Minutes"]();
          var s = date[_ + "Seconds"]();
          var L = date[_ + "Milliseconds"]();
          var o = utc ? 0 : date.getTimezoneOffset();
          var W = this.getWeek(date);
          var N = this.getDayOfWeek(date);
          var flags = {
            d: d,
            dd: this.pad(d),
            ddd: this.dateFormat.i18n.dayNames[D],
            dddd: this.dateFormat.i18n.dayNames[D + 7],
            m: m + 1,
            mm: this.pad(m + 1),
            mmm: this.dateFormat.i18n.monthNames[m],
            mmmm: this.dateFormat.i18n.monthNames[m + 12],
            yy: String(y).slice(2),
            yyyy: y,
            h: H % 12 || 12,
            hh: this.pad(H % 12 || 12),
            H: H,
            HH: this.pad(H),
            M: M,
            MM: this.pad(M),
            s: s,
            ss: this.pad(s),
            l: this.pad(L, 3),
            L: this.pad(Math.round(L / 10)),
            t:
              H < 12
                ? this.dateFormat.i18n.timeNames[0]
                : this.dateFormat.i18n.timeNames[1],
            tt:
              H < 12
                ? this.dateFormat.i18n.timeNames[2]
                : this.dateFormat.i18n.timeNames[3],
            T:
              H < 12
                ? this.dateFormat.i18n.timeNames[4]
                : this.dateFormat.i18n.timeNames[5],
            TT:
              H < 12
                ? this.dateFormat.i18n.timeNames[6]
                : this.dateFormat.i18n.timeNames[7],
            Z: gmt
              ? "GMT"
              : utc
              ? "UTC"
              : (String(date).match(timezone) || [""])
                  .pop()
                  .replace(timezoneClip, ""),
            o:
              (o > 0 ? "-" : "+") +
              this.pad(
                Math.floor(Math.abs(o) / 60) * 100 + (Math.abs(o) % 60),
                4
              ),
            S: ["th", "st", "nd", "rd"][
              d % 10 > 3 ? 0 : (((d % 100) - (d % 10) != 10) * d) % 10
            ],
            W: W,
            N: N
          };

          return mask.replace(token, function(match) {
            if (match in flags) {
              return flags[match];
            }
            return match.slice(1, match.length - 1);
          });
        };
      })();

      this.dateFormat.masks = {
        default: "ddd mmm dd yyyy HH:MM:ss",
        shortDate: "m/d/yy",
        mediumDate: "mmm d, yyyy",
        longDate: "mmmm d, yyyy",
        fullDate: "dddd, mmmm d, yyyy",
        shortTime: "h:MM TT",
        mediumTime: "h:MM:ss TT",
        longTime: "h:MM:ss TT Z",
        isoDate: "yyyy-mm-dd",
        isoTime: "HH:MM:ss",
        isoDateTime: "yyyy-mm-dd'T'HH:MM:sso",
        isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'",
        expiresHeaderFormat: "ddd, dd mmm yyyy HH:MM:ss Z"
      };

      // Internationalization strings
      this.dateFormat.i18n = {
        dayNames: [
          "Sun",
          "Mon",
          "Tue",
          "Wed",
          "Thu",
          "Fri",
          "Sat",
          "Sunday",
          "Monday",
          "Tuesday",
          "Wednesday",
          "Thursday",
          "Friday",
          "Saturday"
        ],
        monthNames: [
          "Jan",
          "Feb",
          "Mar",
          "Apr",
          "May",
          "Jun",
          "Jul",
          "Aug",
          "Sep",
          "Oct",
          "Nov",
          "Dec",
          "January",
          "February",
          "March",
          "April",
          "May",
          "June",
          "July",
          "August",
          "September",
          "October",
          "November",
          "December"
        ],
        timeNames: ["a", "p", "am", "pm", "A", "P", "AM", "PM"]
      };

      this.dateUtil = {
        isSame: (date1, date2, granularity) => {
          let dt1 = new Date(date1);
          let dt2 = new Date(date2);
          if (granularity === "date") {
            dt1.setHours(0, 0, 0, 0);
            dt2.setHours(0, 0, 0, 0);
          }
          return dt1.getTime() === dt2.getTime();
        },
        daysInMonth: (year, month) => {
          return new Date(year, month, 0).getDate();
        },
        weekNumber: date => {
          return this.getWeek(date);
        },
        format: (date, mask) => {
          return this.dateFormat(date, mask);
        },
        nextMonth: date => {
          let nextMonthDate = new Date(date.getTime());
          nextMonthDate.setDate(1);
          nextMonthDate.setMonth(nextMonthDate.getMonth() + 1);
          return nextMonthDate;
        },
        prevMonth: date => {
          let prevMonthDate = new Date(date.getTime());
          prevMonthDate.setDate(1);
          prevMonthDate.setMonth(prevMonthDate.getMonth() - 1);
          return prevMonthDate;
        },
        validateDateRange: (newDate, min, max) => {
          let max_date = new Date(max);
          let min_date = new Date(min);

          if (max && newDate.getTime() > max_date.getTime()) {
            return max_date;
          }

          if (min && newDate.getTime() < min_date.getTime()) {
            return min_date;
          }

          return newDate;
        },
        localeData: options => {
          let default_locale = {
            direction: "ltr",
            format: "mm/dd/yyyy",
            separator: " - ",
            applyLabel: "Apply",
            cancelLabel: "Cancel",
            weekLabel: "W",
            customRangeLabel: "Custom Range",
            daysOfWeek: this.dateFormat.i18n.dayNames
              .slice(0, 7)
              .map(d => d.substring(0, 2)),
            monthNames: this.dateFormat.i18n.monthNames.slice(0, 12),
            firstDay: 0
          };

          return { ...default_locale, ...options };
        },
        yearMonth: date => {
          let month = date.getMonth() + 1;
          return date.getFullYear() + (month < 10 ? "0" : "") + month;
        },
        isValidDate: d => {
          return d instanceof Date && !isNaN(d);
        }
      };
    },
    methods: {
      pad(val, len) {
        val = String(val);
        len = len || 2;
        while (val.length < len) {
          val = "0" + val;
        }
        return val;
      },
      getWeek(date) {
        var targetThursday = new Date(
          date.getFullYear(),
          date.getMonth(),
          date.getDate()
        );

        targetThursday.setDate(
          targetThursday.getDate() - ((targetThursday.getDay() + 6) % 7) + 3
        );

        var firstThursday = new Date(targetThursday.getFullYear(), 0, 4);

        firstThursday.setDate(
          firstThursday.getDate() - ((firstThursday.getDay() + 6) % 7) + 3
        );

        var ds =
          targetThursday.getTimezoneOffset() -
          firstThursday.getTimezoneOffset();
        targetThursday.setHours(targetThursday.getHours() - ds);

        var weekDiff = (targetThursday - firstThursday) / (86400000 * 7);
        return 1 + Math.floor(weekDiff);
      },
      updateValues(values) {
        this.dateRange.startDate = this.dateUtil.format(
          values.startDate,
          "yyyy-mm-dd HH:MM:ss"
        );
        this.dateRange.endDate = this.dateUtil.format(
          values.endDate,
          "yyyy-mm-dd HH:MM:ss"
        );
      },
      getDayOfWeek(date) {
        var dow = date.getDay();
        if (dow === 0) {
          dow = 7;
        }
        return dow;
      },
      kindOf(val) {
        if (val === null) {
          return "null";
        }

        if (val === undefined) {
          return "undefined";
        }

        if (typeof val !== "object") {
          return typeof val;
        }

        if (Array.isArray(val)) {
          return "array";
        }

        return {}.toString
          .call(val)
          .slice(8, -1)
          .toLowerCase();
      },
      checkOpen(open) {
        console.log("event: open", open);
      },
      setDateFormat(classes, date) {
        let yesterday = new Date();
        let d1 = this.dateUtil.format(date, "isoDate");
        let d2 = this.dateUtil.format(
          yesterday.setDate(yesterday.getDate() - 1),
          "isoDate"
        );
        if (!classes.disabled) {
          classes.disabled = d1 === d2;
        }
        return classes;
      }
    }
  };
</script>

<!-- demo.name -->
<!-- date-picker.vue -->
```
