浏览代码

fix(DatePicker): 修复Taro使用场景报错 (#1014)

* fix: 修复 DatePicker 调用失败问题

Co-authored-by: yangxiaolu3 <yangxiaolu1993@qq.com>
yangxiaolu1993 3 年之前
父节点
当前提交
b87bb2d2e4

+ 276 - 29
src/packages/__VUE/datepicker/index.taro.vue

@@ -1,52 +1,299 @@
 <template>
-  <picker
-    mode="date"
-    @change="onChange"
-    :value="value"
-    :start="start"
-    :end="end"
-    :fields="fields"
-  >
-    <slot></slot>
-  </picker>
+  <nut-picker
+    :visible="show"
+    @close="closeHandler"
+    :list-data="columns"
+    @change="changeHandler"
+    :title="title"
+    @confirm="confirm"
+  ></nut-picker>
 </template>
 <script lang="ts">
-import { toRefs } from 'vue';
+import { toRefs, watch, computed, reactive, onMounted } from 'vue';
+
+import nutPicker from '../picker/index.taro.vue';
 import { createComponent } from '../../utils/create';
-const { create } = createComponent('datepicker');
-import picker from '../picker/index.vue';
+const { componentName, create } = createComponent('datepicker');
+const currentYear = new Date().getFullYear();
+function isDate(val: Date): val is Date {
+  return Object.prototype.toString.call(val) === '[object Date]' && !isNaN(val.getTime());
+}
+
+const zhCNType = {
+  day: '日',
+  year: '年',
+  month: '月',
+  hour: '时',
+  minute: '分',
+  seconds: '秒'
+};
 export default create({
   components: {
-    [picker.name]: [picker]
+    nutPicker
   },
   props: {
-    value: {
-      type: String,
-      default: new Date()
+    modelValue: null,
+    visible: {
+      type: Boolean,
+      default: false
     },
-    start: {
+    title: {
       type: String,
-      default: '1970-01-01'
+      default: ''
     },
-    end: {
+    type: {
       type: String,
-      default: '2999-01-01'
+      default: 'date'
     },
-    fields: {
-      type: String,
-      default: 'day'
+    isShowChinese: {
+      type: Boolean,
+      default: true
+    },
+    minuteStep: {
+      type: Number,
+      default: 1
+    },
+    minDate: {
+      type: Date,
+      default: () => new Date(currentYear - 10, 0, 1),
+      validator: isDate
+    },
+    maxDate: {
+      type: Date,
+      default: () => new Date(currentYear + 10, 11, 31),
+      validator: isDate
     }
   },
-  emits: ['onChange'],
+  emits: ['click', 'update:visible', 'confirm'],
 
   setup(props, { emit }) {
-    const onChange = (e: any) => {
-      emit('onChange', e.detail.value);
+    const state = reactive({
+      show: false,
+      currentDate: new Date(),
+      title: props.title
+    });
+    const formatValue = (value: Date) => {
+      if (!isDate(value)) {
+        value = props.minDate;
+      }
+      let timestmp = Math.max(value.getTime(), props.minDate.getTime());
+      timestmp = Math.min(timestmp, props.maxDate.getTime());
+
+      return new Date(timestmp);
+    };
+
+    function getMonthEndDay(year: number, month: number): number {
+      return 32 - new Date(year, month - 1, 32).getDate();
+    }
+    const getBoundary = (type: string, value: Date) => {
+      const boundary = props[`${type}Date`];
+      const year = boundary.getFullYear();
+      let month = 1;
+      let date = 1;
+      let hour = 0;
+      let minute = 0;
+
+      if (type === 'max') {
+        month = 12;
+        date = getMonthEndDay(value.getFullYear(), value.getMonth() + 1);
+        hour = 23;
+        minute = 59;
+      }
+      const seconds = minute;
+      if (value.getFullYear() === year) {
+        month = boundary.getMonth() + 1;
+        if (value.getMonth() + 1 === month) {
+          date = boundary.getDate();
+          if (value.getDate() === date) {
+            hour = boundary.getHours();
+            if (value.getHours() === hour) {
+              minute = boundary.getMinutes();
+            }
+          }
+        }
+      }
+
+      return {
+        [`${type}Year`]: year,
+        [`${type}Month`]: month,
+        [`${type}Date`]: date,
+        [`${type}Hour`]: hour,
+        [`${type}Minute`]: minute,
+        [`${type}Seconds`]: seconds
+      };
+    };
+
+    const ranges = computed(() => {
+      const { maxYear, maxDate, maxMonth, maxHour, maxMinute, maxSeconds } = getBoundary('max', state.currentDate);
+
+      const { minYear, minDate, minMonth, minHour, minMinute, minSeconds } = getBoundary('min', state.currentDate);
+
+      let result = [
+        {
+          type: 'year',
+          range: [minYear, maxYear]
+        },
+        {
+          type: 'month',
+          range: [minMonth, maxMonth]
+        },
+        {
+          type: 'day',
+          range: [minDate, maxDate]
+        },
+        {
+          type: 'hour',
+          range: [minHour, maxHour]
+        },
+        {
+          type: 'minute',
+          range: [minMinute, maxMinute]
+        },
+        {
+          type: 'seconds',
+          range: [minSeconds, maxSeconds]
+        }
+      ];
+
+      switch (props.type) {
+        case 'date':
+          result = result.slice(0, 3);
+          break;
+        case 'datetime':
+          result = result.slice(0, 5);
+          break;
+        case 'time':
+          result = result.slice(3, 6);
+          break;
+        case 'month-day':
+          result = result.slice(1, 3);
+          break;
+        case 'datehour':
+          result = result.slice(0, 4);
+          break;
+      }
+      return result;
+    });
+
+    const changeHandler = (val: string[]) => {
+      if (['date', 'datetime'].includes(props.type)) {
+        let formatDate = [];
+        if (props.isShowChinese) {
+          formatDate = val.map((res: string) => {
+            return Number(res.slice(0, res.length - 1));
+          }) as any;
+        } else {
+          formatDate = val;
+        }
+        let date: Date;
+        if (props.type === 'date') {
+          state.currentDate = formatValue(
+            new Date(
+              formatDate[0],
+              formatDate[1] - 1,
+              Math.min(formatDate[2], getMonthEndDay(formatDate[0], formatDate[1]))
+            )
+          );
+        } else if (props.type === 'datetime') {
+          state.currentDate = formatValue(
+            new Date(
+              formatDate[0],
+              formatDate[1] - 1,
+              Math.min(formatDate[2], getMonthEndDay(formatDate[0], formatDate[1])),
+              formatDate[3],
+              formatDate[4]
+            )
+          );
+        }
+      }
+    };
+
+    const generateValue = (min: number, max: number, val: number, type: string) => {
+      // if (!(max > min)) return;
+      const arr: Array<number | string> = [];
+      let index = 0;
+      while (min <= max) {
+        if (props.isShowChinese) {
+          arr.push(min + zhCNType[type]);
+        } else {
+          arr.push(min);
+        }
+
+        if (type === 'minute') {
+          min += props.minuteStep;
+        } else {
+          min++;
+        }
+
+        if (min <= val) {
+          index++;
+        }
+      }
+
+      return { values: arr, defaultIndex: index };
+    };
+
+    const getDateIndex = (type: string) => {
+      if (type === 'year') {
+        return state.currentDate.getFullYear();
+      } else if (type === 'month') {
+        return state.currentDate.getMonth() + 1;
+      } else if (type === 'day') {
+        return state.currentDate.getDate();
+      } else if (type === 'hour') {
+        return state.currentDate.getHours();
+      } else if (type === 'minute') {
+        return state.currentDate.getMinutes();
+      } else if (type === 'seconds') {
+        return state.currentDate.getSeconds();
+      }
+      return 0;
+    };
+
+    const columns = computed(() => {
+      // console.log(ranges.value);
+      const val = ranges.value.map((res) => {
+        return generateValue(res.range[0], res.range[1], getDateIndex(res.type), res.type);
+      });
+      return val;
+    });
+    const handleClick = (event: Event) => {
+      emit('click', event);
+    };
+
+    const closeHandler = () => {
+      emit('update:visible', false);
+    };
+
+    const confirm = (val: Event) => {
+      emit('update:visible', false);
+      emit('confirm', val);
     };
+
+    onMounted(() => {
+      state.currentDate = formatValue(props.modelValue);
+    });
+
+    watch(
+      () => props.title,
+      (val) => {
+        state.title = val;
+      }
+    );
+
+    watch(
+      () => props.visible,
+      (val) => {
+        state.show = val;
+      }
+    );
+
     return {
+      ...toRefs(state),
+      changeHandler,
+      closeHandler,
       confirm,
-      onChange,
-      ...toRefs(props)
+      columns
     };
   }
 });

+ 4 - 1
src/packages/__VUE/picker/doc.md

@@ -7,7 +7,10 @@
 ## 安装
 ```javascript
 import { createApp } from 'vue';
-import { Picker,Popup } from '@nutui/nutui';
+// vue
+import { Picker, Popup } from '@nutui/nutui';
+// taro
+import { Picker, Popup } from '@nutui/nutui-taro';
 
 const app = createApp();
 app.use(Picker);

+ 110 - 11
src/sites/mobile-taro/vue/src/dentry/pages/datepicker/index.vue

@@ -1,26 +1,125 @@
 <template>
   <div class="demo">
-    <h2>日期选择器</h2>
-    <nut-datepicker @onChange="onChange">
-      <nut-cell title="请选择日期" :desc="desc"></nut-cell>
-    </nut-datepicker>
+    <h2>每列不显示中文</h2>
+    <nut-cell title="日期选择" :desc="desc1" @click="open(0)"></nut-cell>
+    <h2>限制开始结束时间</h2>
+    <nut-cell title="日期选择" :desc="desc2" @click="open(1)"></nut-cell>
+    <h2>限制开始结束时间(有默认值)</h2>
+    <nut-cell title="日期时间选择" :desc="desc3" @click="open(2)"></nut-cell>
+    <h2>限制开始结束小时</h2>
+    <nut-cell title="时间选择" :desc="desc4" @click="open(3)"></nut-cell>
+    <h2>分钟数递增步长设置</h2>
+    <nut-cell title="时间选择" :desc="desc5" @click="open(4)"></nut-cell>
+
+    <nut-datepicker
+      v-model="currentDate"
+      @confirm="
+        (val) => {
+          confirm(0, val);
+        }
+      "
+      v-model:visible="show"
+      :is-show-chinese="false"
+    ></nut-datepicker>
+    <nut-datepicker
+      v-model="currentDate"
+      title="日期选择"
+      :min-date="minDate"
+      :max-date="maxDate"
+      @confirm="
+        (val) => {
+          confirm(1, val);
+        }
+      "
+      v-model:visible="show2"
+      :is-show-chinese="false"
+    ></nut-datepicker>
+    <nut-datepicker
+      v-model="currentDate"
+      title="日期时间选择"
+      type="datetime"
+      :min-date="minDate"
+      :max-date="maxDate"
+      @confirm="
+        (val) => {
+          confirm(2, val);
+        }
+      "
+      v-model:visible="show3"
+    ></nut-datepicker>
+    <nut-datepicker
+      v-model="currentDate"
+      title="时间选择"
+      type="time"
+      :min-date="minDate"
+      :max-date="maxDate"
+      @confirm="
+        (val) => {
+          confirm(3, val);
+        }
+      "
+      v-model:visible="show4"
+    ></nut-datepicker>
+    <nut-datepicker
+      v-model="currentDate"
+      title="时间选择"
+      type="time"
+      :min-date="minDate"
+      :minute-step="5"
+      :max-date="maxDate"
+      @confirm="
+        (val) => {
+          confirm(4, val);
+        }
+      "
+      v-model:visible="show5"
+    ></nut-datepicker>
   </div>
 </template>
+
 <script lang="ts">
-import { ref } from 'vue';
+import { toRefs, watch, ref } from 'vue';
 export default {
   props: {},
   setup() {
-    const desc = ref();
+    const show = ref(false);
+    const show2 = ref(false);
+    const show3 = ref(false);
+    const show4 = ref(false);
+    const show5 = ref(false);
 
-    const onChange = (value: any) => {
-      desc.value = value;
-    };
+    const showList = [show, show2, show3, show4, show5];
+    const currentDate = new Date(2020, 0, 1);
 
+    const today = currentDate;
+    const desc1 = ref('2020-1-1');
+    const desc2 = ref('2020-1-1');
+    const desc3 = ref('2020年-1月-1日-0时-0分');
+    const desc4 = ref('0时-0分-0秒');
+    const desc5 = ref('0时-0分-0秒');
+    const descList = [desc1, desc2, desc3, desc4, desc5];
     return {
-      desc,
-      onChange
+      show,
+      show2,
+      show3,
+      show4,
+      show5,
+      desc1,
+      desc2,
+      desc3,
+      desc4,
+      desc5,
+      currentDate,
+      minDate: new Date(2020, 0, 1),
+      maxDate: new Date(2025, 10, 1),
+      open: (index: number) => {
+        showList[index].value = true;
+      },
+      confirm: (index: number, val: string[]) => {
+        descList[index].value = val.join('-');
+      }
     };
   }
 };
 </script>
+<style lang="scss" scoped></style>