Browse Source

feat(timeselect): add timeselect component

suzigang 4 years ago
parent
commit
f45f375226

+ 35 - 0
src/config.json

@@ -825,6 +825,41 @@
           "sort": 3,
           "show": true,
           "author": "guoxiaoxiao"
+        },
+        {
+          "version": "3.0.0",
+          "taro": true,
+          "name": "TimeSelect",
+          "type": "component",
+          "cName": "配送时间",
+          "desc": "配送时间",
+          "sort": 4,
+          "show": true,
+          "author": "szg2008"
+        },
+        {
+          "version": "3.0.0",
+          "taro": true,
+          "name": "TimePannel",
+          "type": "component",
+          "cName": "配送时间",
+          "desc": "配送时间",
+          "sort": 5,
+          "show": false,
+          "exportEmpty": true,
+          "author": "szg2008"
+        },
+        {
+          "version": "3.0.0",
+          "taro": true,
+          "name": "TimeDetail",
+          "type": "component",
+          "cName": "配送时间",
+          "desc": "配送时间",
+          "sort": 6,
+          "show": false,
+          "exportEmpty": true,
+          "author": "szg2008"
         }
       ]
     }

+ 40 - 0
src/packages/__VUE/timedetail/demo.vue

@@ -0,0 +1,40 @@
+<template>
+  <div class="demo">
+    <h2>基本用法</h2>
+    <nut-cell @click="handleClick1">
+      <span><label>时间配送</label></span>
+      <div class="selected-option"> {{ val1 }} </div>
+    </nut-cell>
+    <div class="timeselect-wrapper">
+      <nut-timeselect :visible="visible1"></nut-timeselect>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { reactive, toRefs } from 'vue';
+import { createComponent } from '../../utils/create';
+const { createDemo } = createComponent('timeselect');
+export default createDemo({
+  setup() {
+    const state = reactive({
+      visible1: false,
+      val1: ''
+    });
+
+    const handleClick1 = () => {
+      state.visible1 = true;
+    };
+
+    return {
+      ...toRefs(state),
+      handleClick1
+    };
+  }
+});
+</script>
+
+<style lang="scss" scoped>
+.demo {
+}
+</style>

+ 50 - 0
src/packages/__VUE/timedetail/doc.md

@@ -0,0 +1,50 @@
+# TimeSelect 配送时间
+
+### 介绍
+
+用于xxx
+
+### 安装
+
+``` javascript
+import { createApp } from 'vue';
+// vue
+import { TimeSelect } from '@nutui/nutui';
+// taro
+import { TimeSelect } from '@nutui/nutui-taro';
+
+const app = createApp();
+app.use(TimeSelect);
+```
+
+### 基本用法
+
+``` html
+<nut-timeselect></nut-timeselect>
+```
+``` javascript
+setup() {
+    const state = reactive({
+      
+    });
+
+    return { ...toRefs(state) };
+  }
+```
+
+## API
+
+### Prop
+
+| 字段                   | 说明                                                             | 类型    | 默认值 |
+|------------------------|----------------------------------------------------------------|---------|------|
+| height                 | 电梯区域的高度                                                    | Number、String  | `200px`
+
+
+### Event
+
+| 名称  | 说明     | 回调参数    |
+|-------|----------|-------------|
+| click-item | 点击内容 | key: string, item: { id: 0, name: '' } |
+
+

+ 39 - 0
src/packages/__VUE/timedetail/index.scss

@@ -0,0 +1,39 @@
+.nut-timedetail {
+  width: 100%;
+  height: 100%;
+  overflow: auto;
+  padding: 0 5px 50px 13px;
+  &__detail {
+    width: 100%;
+    &__list {
+      &__item {
+        display: inline-block;
+        width: 100px;
+        height: 50px;
+        line-height: 50px;
+        text-align: center;
+        margin-right: 10px;
+        margin-bottom: 10px;
+        background-color: $timedetail-item-bg-color;
+        border-radius: $timedetail-item-border-radius;
+        color: $timedetail-item-text-color;
+        font-size: $timedetail-item-text-font-size;
+        border: 1px solid transparent;
+        font-weight: bold;
+        &--curr {
+          background-color: $timedetail-item-cur-bg-color;
+          border: 1px solid $timedetail-item-cur-border;
+          color: $timedetail-item-cur-text-color;
+        }
+      }
+    }
+    &__time {
+      margin-bottom: 10px;
+      color: $timedetail-time-text-color;
+      font-size: $timedetail-time-font-size;
+    }
+  }
+  &__detail--afternoon {
+    margin-top: 30px;
+  }
+}

+ 89 - 0
src/packages/__VUE/timedetail/index.taro.vue

@@ -0,0 +1,89 @@
+<template>
+  <view :class="classes">
+    <view class="nut-timedetail__detail nut-timedetail__detail--moring">
+      <!-- <view class="nut-timedetail__detail__time">上午</view> -->
+      <view class="nut-timedetail__detail__list">
+        <view
+          :class="getClass(item)"
+          v-for="item in renderData"
+          :key="item"
+          @click="handleTime(item)"
+          >{{ item }}</view
+        >
+      </view>
+    </view>
+  </view>
+</template>
+
+<script lang="ts">
+import { reactive, toRefs, inject, computed } from 'vue';
+import { createComponent } from '../../utils/create';
+const { componentName, create } = createComponent('timedetail');
+export default create({
+  name: 'timedetail',
+  props: {
+    times: {
+      type: Array,
+      default: () => {
+        return [];
+      }
+    },
+    detailKey: {
+      type: [Number, String],
+      default: 0
+    }
+  },
+  emits: ['select'],
+  setup: (props: any, context: any) => {
+    const currentKey = inject('currentKey');
+    const currentTime = inject('currentTime');
+    const muti = inject('muti');
+
+    const state = reactive({
+      currentKey,
+      currentTime: currentTime as any[]
+    });
+
+    const classes = computed(() => {
+      const prefixCls = componentName;
+      return {
+        [prefixCls]: true
+      };
+    });
+
+    const getClass = (item: string) => {
+      let find = state.currentTime.find(
+        (item: any) => item.key == state.currentKey
+      );
+      if (find) {
+        return {
+          'nut-timedetail__detail__list__item': true,
+          'nut-timedetail__detail__list__item--curr':
+            find.list.filter((value: string) => value === item).length > 0
+        };
+      }
+    };
+
+    const renderData = computed(() => {
+      return props.times.find((time: any) => time.key == state.currentKey)[
+        'list'
+      ];
+    });
+
+    const handleTime = (time: string) => {
+      context.emit('select', time);
+    };
+
+    return {
+      classes,
+      ...toRefs(state),
+      getClass,
+      renderData,
+      handleTime
+    };
+  }
+});
+</script>
+<style lang="scss" scoped>
+@import 'index.scss';
+</style>

+ 88 - 0
src/packages/__VUE/timedetail/index.vue

@@ -0,0 +1,88 @@
+<template>
+  <view :class="classes">
+    <view class="nut-timedetail__detail nut-timedetail__detail--moring">
+      <!-- <view class="nut-timedetail__detail__time">上午</view> -->
+      <view class="nut-timedetail__detail__list">
+        <view
+          :class="getClass(item)"
+          v-for="item in renderData"
+          :key="item"
+          @click="handleTime(item)"
+          >{{ item }}</view
+        >
+      </view>
+    </view>
+  </view>
+</template>
+
+<script lang="ts">
+import { reactive, toRefs, inject, computed } from 'vue';
+import { createComponent } from '../../utils/create';
+const { componentName, create } = createComponent('timedetail');
+export default create({
+  name: 'timedetail',
+  props: {
+    times: {
+      type: Array,
+      default: () => {
+        return [];
+      }
+    },
+    detailKey: {
+      type: [Number, String],
+      default: 0
+    }
+  },
+  emits: ['select'],
+  setup: (props: any, context: any) => {
+    const currentKey = inject('currentKey');
+    const currentTime = inject('currentTime');
+
+    const state = reactive({
+      currentKey,
+      currentTime: currentTime as any[]
+    });
+
+    const classes = computed(() => {
+      const prefixCls = componentName;
+      return {
+        [prefixCls]: true
+      };
+    });
+
+    const getClass = (item: string) => {
+      let find = state.currentTime.find(
+        (item: any) => item.key == state.currentKey
+      );
+      if (find) {
+        return {
+          'nut-timedetail__detail__list__item': true,
+          'nut-timedetail__detail__list__item--curr':
+            find.list.filter((value: string) => value === item).length > 0
+        };
+      }
+    };
+
+    const renderData = computed(() => {
+      return props.times.find((time: any) => time.key == state.currentKey)[
+        'list'
+      ];
+    });
+
+    const handleTime = (time: string) => {
+      context.emit('select', time);
+    };
+
+    return {
+      classes,
+      ...toRefs(state),
+      getClass,
+      renderData,
+      handleTime
+    };
+  }
+});
+</script>
+<style lang="scss" scoped>
+@import 'index.scss';
+</style>

+ 40 - 0
src/packages/__VUE/timepannel/demo.vue

@@ -0,0 +1,40 @@
+<template>
+  <div class="demo">
+    <h2>基本用法</h2>
+    <nut-cell @click="handleClick1">
+      <span><label>时间配送</label></span>
+      <div class="selected-option"> {{ val1 }} </div>
+    </nut-cell>
+    <div class="timeselect-wrapper">
+      <nut-timeselect :visible="visible1"></nut-timeselect>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { reactive, toRefs } from 'vue';
+import { createComponent } from '../../utils/create';
+const { createDemo } = createComponent('timeselect');
+export default createDemo({
+  setup() {
+    const state = reactive({
+      visible1: false,
+      val1: ''
+    });
+
+    const handleClick1 = () => {
+      state.visible1 = true;
+    };
+
+    return {
+      ...toRefs(state),
+      handleClick1
+    };
+  }
+});
+</script>
+
+<style lang="scss" scoped>
+.demo {
+}
+</style>

+ 50 - 0
src/packages/__VUE/timepannel/doc.md

@@ -0,0 +1,50 @@
+# TimeSelect 配送时间
+
+### 介绍
+
+用于xxx
+
+### 安装
+
+``` javascript
+import { createApp } from 'vue';
+// vue
+import { TimeSelect } from '@nutui/nutui';
+// taro
+import { TimeSelect } from '@nutui/nutui-taro';
+
+const app = createApp();
+app.use(TimeSelect);
+```
+
+### 基本用法
+
+``` html
+<nut-timeselect></nut-timeselect>
+```
+``` javascript
+setup() {
+    const state = reactive({
+      
+    });
+
+    return { ...toRefs(state) };
+  }
+```
+
+## API
+
+### Prop
+
+| 字段                   | 说明                                                             | 类型    | 默认值 |
+|------------------------|----------------------------------------------------------------|---------|------|
+| height                 | 电梯区域的高度                                                    | Number、String  | `200px`
+
+
+### Event
+
+| 名称  | 说明     | 回调参数    |
+|-------|----------|-------------|
+| click-item | 点击内容 | key: string, item: { id: 0, name: '' } |
+
+

+ 16 - 0
src/packages/__VUE/timepannel/index.scss

@@ -0,0 +1,16 @@
+.nut-timepannel {
+  display: flex;
+  width: 140px;
+  height: 40px;
+  padding: 15px;
+  align-items: center;
+  justify-content: center;
+  color: $timepannel-text-color;
+  font-size: $timepannel-font-size;
+  box-sizing: border-box;
+  &--curr {
+    background-color: $timepannel-cur-bg-color;
+    color: $timepannel-cur-text-color;
+    font-weight: bold;
+  }
+}

+ 51 - 0
src/packages/__VUE/timepannel/index.taro.vue

@@ -0,0 +1,51 @@
+<template>
+  <view :class="classes" @click="handlePannel(pannelKey)">{{ name }}</view>
+</template>
+
+<script lang="ts">
+import { reactive, toRefs, inject, computed } from 'vue';
+import { createComponent } from '../../utils/create';
+const { componentName, create } = createComponent('timepannel');
+export default create({
+  name: 'timepannel',
+  props: {
+    name: {
+      type: String,
+      default: ''
+    },
+    pannelKey: {
+      type: [Number, String],
+      default: 0
+    }
+  },
+  emits: ['change'],
+  setup: (props: any, context: any) => {
+    const currentKey = inject('currentKey');
+
+    const state = reactive({
+      currentKey
+    });
+
+    const classes = computed(() => {
+      const prefixCls = componentName;
+      return {
+        [prefixCls]: true,
+        'nut-timepannel--curr': state.currentKey == props.pannelKey
+      };
+    });
+
+    const handlePannel = (pannelKey: number | string) => {
+      context.emit('change', pannelKey);
+    };
+
+    return {
+      ...toRefs(state),
+      classes,
+      handlePannel
+    };
+  }
+});
+</script>
+<style lang="scss" scoped>
+@import 'index.scss';
+</style>

+ 51 - 0
src/packages/__VUE/timepannel/index.vue

@@ -0,0 +1,51 @@
+<template>
+  <view :class="classes" @click="handlePannel(pannelKey)">{{ name }}</view>
+</template>
+
+<script lang="ts">
+import { reactive, toRefs, inject, computed } from 'vue';
+import { createComponent } from '../../utils/create';
+const { componentName, create } = createComponent('timepannel');
+export default create({
+  name: 'timepannel',
+  props: {
+    name: {
+      type: String,
+      default: ''
+    },
+    pannelKey: {
+      type: [Number, String],
+      default: 0
+    }
+  },
+  emits: ['change'],
+  setup: (props: any, context: any) => {
+    const currentKey = inject('currentKey');
+
+    const state = reactive({
+      currentKey
+    });
+
+    const classes = computed(() => {
+      const prefixCls = componentName;
+      return {
+        [prefixCls]: true,
+        'nut-timepannel--curr': state.currentKey == props.pannelKey
+      };
+    });
+
+    const handlePannel = (pannelKey: number | string) => {
+      context.emit('change', pannelKey);
+    };
+
+    return {
+      ...toRefs(state),
+      classes,
+      handlePannel
+    };
+  }
+});
+</script>
+<style lang="scss" scoped>
+@import 'index.scss';
+</style>

+ 188 - 0
src/packages/__VUE/timeselect/demo.vue

@@ -0,0 +1,188 @@
+<template>
+  <div class="demo">
+    <h2>基本用法</h2>
+    <nut-cell @click="handleClick1">
+      <span><label>请选择配送时间</label></span>
+    </nut-cell>
+    <nut-timeselect
+      v-model:visible="visible1"
+      height="50%"
+      :current-key="currentKey1"
+      :current-time="currentTime1"
+      @selected="handleSelected1"
+    >
+      <template #pannel>
+        <nut-timepannel
+          name="2月23日(今天)"
+          pannel-key="0"
+          @change="handleChange1"
+        ></nut-timepannel>
+        <nut-timepannel
+          name="2月24日(星期三)"
+          pannel-key="1"
+          @change="handleChange1"
+        ></nut-timepannel>
+      </template>
+      <template #detail>
+        <nut-timedetail :times="times1" @select="selectTime1"></nut-timedetail>
+      </template>
+    </nut-timeselect>
+    <h2>可选择多个日期时间</h2>
+    <nut-cell @click="handleClick2">
+      <span><label>请选择配送时间</label></span>
+    </nut-cell>
+    <nut-timeselect
+      v-model:visible="visible2"
+      height="50%"
+      :current-key="currentKey2"
+      :current-time="currentTime2"
+      @selected="handleSelected2"
+    >
+      <template #pannel>
+        <nut-timepannel
+          name="2月23日(今天)"
+          pannel-key="0"
+          @change="handleChange2"
+        ></nut-timepannel>
+        <nut-timepannel
+          name="2月24日(星期三)"
+          pannel-key="1"
+          @change="handleChange2"
+        ></nut-timepannel>
+      </template>
+      <template #detail>
+        <nut-timedetail :times="times2" @select="selectTime2"></nut-timedetail>
+      </template>
+    </nut-timeselect>
+  </div>
+</template>
+
+<script lang="ts">
+import { reactive, toRefs, getCurrentInstance, onMounted } from 'vue';
+import { createComponent } from '../../utils/create';
+const { createDemo } = createComponent('timeselect');
+export default createDemo({
+  setup() {
+    const { proxy } = getCurrentInstance() as any;
+    const state = reactive({
+      visible1: false,
+      currentKey1: 0,
+      currentTime1: [] as any[],
+      times1: [
+        {
+          key: 0,
+          list: ['9:00-10:00', '10:00-11:00', '11:00-12:00']
+        },
+        {
+          key: 1,
+          list: ['9:00-10:00', '10:00-11:00']
+        }
+      ],
+      visible2: false,
+      currentKey2: 0,
+      currentTime2: [] as any[],
+      times2: [
+        {
+          key: 0,
+          list: ['9:00-10:00', '10:00-11:00', '11:00-12:00']
+        },
+        {
+          key: 1,
+          list: ['9:00-10:00', '10:00-11:00']
+        }
+      ]
+    });
+
+    const handleChange1 = (pannelKey: number) => {
+      state.currentKey1 = pannelKey;
+      state.currentTime1 = [];
+      state.currentTime1.push({
+        key: state.currentKey1,
+        list: []
+      });
+    };
+
+    const handleClick1 = () => {
+      state.visible1 = true;
+    };
+
+    const selectTime1 = (item: string) => {
+      let curTimeIndex = state.currentTime1[0]['list'].findIndex(
+        (time: string) => time === item
+      );
+      if (curTimeIndex === -1) {
+        state.currentTime1[0]['list'].push(item);
+      } else {
+        state.currentTime1[0]['list'].splice(curTimeIndex, 1);
+      }
+    };
+
+    const handleSelected1 = (obj: any) => {
+      proxy.$toast.text(`您选择了:${JSON.stringify(obj)}`);
+    };
+
+    const handleChange2 = (pannelKey: number) => {
+      state.currentKey2 = pannelKey;
+      let curTime = state.currentTime2.find(
+        (item: any) => item.key == pannelKey
+      );
+      if (!curTime) {
+        state.currentTime2.push({
+          key: pannelKey,
+          list: []
+        });
+      }
+    };
+
+    const handleClick2 = () => {
+      state.visible2 = true;
+    };
+
+    const selectTime2 = (item: string) => {
+      let findIndex = state.currentTime2.findIndex(
+        (item: any) => item.key == state.currentKey2
+      );
+      let curTimeIndex = state.currentTime2[findIndex]['list'].findIndex(
+        (time: string) => time === item
+      );
+      if (curTimeIndex === -1) {
+        state.currentTime2[findIndex]['list'].push(item);
+      } else {
+        state.currentTime2[findIndex]['list'].splice(curTimeIndex, 1);
+      }
+    };
+
+    const handleSelected2 = (obj: any) => {
+      proxy.$toast.text(`您选择了:${JSON.stringify(obj)}`);
+    };
+
+    onMounted(() => {
+      state.currentTime1.push({
+        key: state.currentKey1,
+        list: []
+      });
+      state.currentTime2.push({
+        key: state.currentKey2,
+        list: []
+      });
+    });
+
+    return {
+      ...toRefs(state),
+      handleChange1,
+      handleSelected1,
+      selectTime1,
+      handleClick1,
+      handleChange2,
+      handleSelected2,
+      selectTime2,
+      handleClick2
+    };
+  }
+});
+</script>
+
+<style lang="scss" scoped>
+.demo {
+}
+</style>

+ 201 - 0
src/packages/__VUE/timeselect/doc.md

@@ -0,0 +1,201 @@
+# TimeSelect 配送时间
+
+### 介绍
+
+用于配送时间选择
+
+### 安装
+
+``` javascript
+import { createApp } from 'vue';
+// vue
+import { TimeSelect, TimePannel, TimeDetail, Popup } from '@nutui/nutui';
+// taro
+import { TimeSelect, TimePannel, TimeDetail, Popup } from '@nutui/nutui-taro';
+
+const app = createApp();
+app.use(TimeSelect).use(TimePannel).use(TimeDetail).use(Popup);
+```
+
+### 基本用法
+
+``` html
+<nut-timeselect v-model:visible="visible1" height="50%" :current-key="currentKey1" :current-time="currentTime1" @select="handleSelected1">
+  <template #pannel>
+    <nut-timepannel name="2月23日(今天)" pannel-key="0" @change="handleChange1"></nut-timepannel>
+    <nut-timepannel name="2月24日(星期三)" pannel-key="1" @change="handleChange1"></nut-timepannel>
+  </template>
+  <template #detail>
+    <nut-timedetail :times="times1" @select="selectTime1"></nut-timedetail>
+  </template>
+</nut-timeselect>
+```
+``` javascript
+setup() {
+    const state = reactive({
+      visible1: false,
+      currentKey1: 0,
+      currentTime1: [] as any[],
+      times1: [
+        {
+          key: 0,
+          list: ['9:00-10:00', '10:00-11:00', '11:00-12:00']
+        },
+        {
+          key: 1,
+          list: ['9:00-10:00', '10:00-11:00']
+        },
+      ],
+    });
+
+    const handleChange1 = (pannelKey: number) => {
+      state.currentKey1 = pannelKey;
+      state.currentTime1 = [];
+      state.currentTime1.push({
+        key: state.currentKey1,
+        list: []
+      });
+    };
+
+    const handleClick1 = () => {
+      state.visible1 = true;
+    };
+
+    const selectTime1 = (item: string) => {
+      let curTimeIndex = state.currentTime1[0]['list'].findIndex((time: string) => time === item);
+      if(curTimeIndex === -1) {
+        state.currentTime1[0]['list'].push(item);
+      } else {
+        state.currentTime1[0]['list'].splice(curTimeIndex, 1);
+      }
+    };
+
+    const handleSelected1 = (obj: any) => {
+      proxy.$toast.text(`您选择了:${JSON.stringify(obj)}`);
+    };
+
+    return { 
+      ...toRefs(state), 
+      handleChange1,
+      handleSelected1,
+      selectTime1,
+      handleClick1, 
+    };
+  }
+```
+
+### 可选择多个日期时间
+
+``` html
+<nut-timeselect v-model:visible="visible2" height="50%" :current-key="currentKey2" :current-time="currentTime2" @select="handleSelected2">
+  <template #pannel>
+    <nut-timepannel name="2月23日(今天)" pannel-key="0" @change="handleChange2"></nut-timepannel>
+    <nut-timepannel name="2月24日(星期三)" pannel-key="1" @change="handleChange2"></nut-timepannel>
+  </template>
+  <template #detail>
+    <nut-timedetail :times="times2" @select="selectTime2"></nut-timedetail>
+  </template>
+</nut-timeselect>
+```
+``` javascript
+setup() {
+    const state = reactive({
+      visible2: false,
+      currentKey2: 0,
+      currentTime2: [] as any[],
+      times2: [
+        {
+          key: 0,
+          list: ['9:00-10:00', '10:00-11:00', '11:00-12:00']
+        },
+        {
+          key: 1,
+          list: ['9:00-10:00', '10:00-11:00']
+        },
+      ]
+    });
+
+    const handleChange2 = (pannelKey: number) => {
+      state.currentKey2 = pannelKey;
+      let curTime = state.currentTime2.find((item: any) => item.key == pannelKey);
+      if(!curTime) {
+        state.currentTime2.push({
+          key: pannelKey,
+          list: []
+        });
+      }
+    };
+
+    const handleClick2 = () => {
+      state.visible2 = true;
+    };
+
+    const selectTime2 = (item: string) => {
+      let findIndex = state.currentTime2.findIndex((item: any) => item.key == state.currentKey2);
+      let curTimeIndex = state.currentTime2[findIndex]['list'].findIndex((time: string) => time === item);
+      if(curTimeIndex === -1) {
+        state.currentTime2[findIndex]['list'].push(item);
+      } else {
+        state.currentTime2[findIndex]['list'].splice(curTimeIndex, 1);
+      }
+    };
+
+    const handleSelected2 = (obj: any) => {
+      proxy.$toast.text(`您选择了:${JSON.stringify(obj)}`);
+    };
+
+    return { 
+      ...toRefs(state), 
+      handleChange2,
+      handleSelected2,
+      selectTime2,
+      handleClick2, 
+    };
+  }
+```
+
+## API
+
+### TimeSelect Prop
+
+| 字段                   | 说明                                                             | 类型    | 默认值 |
+|------------------------|----------------------------------------------------------------|---------|------|
+| visible                 | 是否显示弹层                                                    | Boolean  | `false`
+| height                 | 弹层的高度                                                    | String  | `20%`
+| title                 | 弹层标题                                                    | String  | `取件时间`
+| current-key                 | 唯一标识                                                    | String、Number  | `0`
+| current-time                 | 当前选择的时间,数组元素包含:key: string; list: string[]      | Array  | `[]`
+
+### TimePannel Prop
+
+| 字段                   | 说明                                                             | 类型    | 默认值 |
+|------------------------|----------------------------------------------------------------|---------|------|
+| name                 | 显示的名称                                                    | String  | ``
+| pannel-key           | 唯一标识,和 current-key一起标识当前选择的天                      | Number、String  | `0`
+
+### TimeDetail Prop
+
+| 字段                   | 说明                                                             | 类型    | 默认值 |
+|------------------------|----------------------------------------------------------------|---------|------|
+| times                 | 可选择的时间,数组元素同 `current-time`                              | Array  | `[]`
+
+
+### TimeSelect Event
+
+| 名称  | 说明     | 回调参数    |
+|-------|----------|-------------|
+| select | 关闭遮罩之后的回调 | key: string | number, list: [] |
+
+### TimePannel Event
+
+| 名称  | 说明     | 回调参数    |
+|-------|----------|-------------|
+| change | 点击内容的回调 | pannelKey: string | number |
+
+### TimeDetail Event
+
+| 名称  | 说明     | 回调参数    |
+|-------|----------|-------------|
+| select | 点击时间的回调 | time: string |
+
+

+ 34 - 0
src/packages/__VUE/timeselect/index.scss

@@ -0,0 +1,34 @@
+.nut-timeselect {
+  width: 100%;
+  height: 100%;
+  position: relative;
+  overflow: hidden;
+  &__title {
+    width: 100%;
+    height: 50px;
+    line-height: 50px;
+    margin-bottom: 10px;
+    font-size: $timeselect-title-font-size;
+    color: $timeselect-title-color;
+    text-align: center;
+    &__fixed {
+      position: fixed;
+      width: 100%;
+      height: 50px;
+    }
+  }
+  &__content {
+    width: 100%;
+    display: flex;
+    &__pannel {
+      width: 140px;
+      height: 308px;
+      overflow: auto;
+      background-color: $timeselect-pannel-bg-color;
+    }
+    &__detail {
+      width: calc(100% - 140px);
+      height: 308px;
+    }
+  }
+}

+ 103 - 0
src/packages/__VUE/timeselect/index.taro.vue

@@ -0,0 +1,103 @@
+<template>
+  <nut-popup
+    position="bottom"
+    closeable
+    round
+    :visible="visible"
+    :style="popStyle"
+    @click-overlay="close"
+    @click-close-icon="close"
+  >
+    <view :class="classes">
+      <view class="nut-timeselect__title">
+        <view class="nut-timeselect__title__fixed">
+          {{ title }}
+        </view>
+      </view>
+      <view class="nut-timeselect__content">
+        <view class="nut-timeselect__content__pannel">
+          <slot name="pannel"></slot>
+        </view>
+        <view class="nut-timeselect__content__detail">
+          <slot name="detail"></slot>
+        </view>
+      </view>
+    </view>
+  </nut-popup>
+</template>
+<script lang="ts">
+import { computed, provide } from 'vue';
+import { createComponent } from '../../utils/create';
+const { componentName, create } = createComponent('timeselect');
+export default create({
+  props: {
+    visible: {
+      type: Boolean,
+      defalut: false
+    },
+    height: {
+      type: [String],
+      default: '20%'
+    },
+    title: {
+      type: String,
+      default: '取件时间'
+    },
+    currentKey: {
+      type: [Number, String],
+      default: 0
+    },
+    currentTime: {
+      type: Array,
+      default: () => {
+        return [];
+      }
+    },
+    muti: {
+      type: [Boolean],
+      default: false
+    }
+  },
+  emits: ['update:visible', 'selected'],
+  setup: (props: any, context: any) => {
+    const classes = computed(() => {
+      const prefixCls = componentName;
+      return {
+        [prefixCls]: true
+      };
+    });
+
+    const popStyle = computed(() => {
+      return {
+        width: '100%',
+        height: props.height
+      };
+    });
+
+    const currentKey = computed(() => props.currentKey);
+
+    const currentTime = computed(() => props.currentTime);
+
+    const muti = computed(() => props.muti);
+
+    const close = () => {
+      context.emit('update:visible', false);
+      context.emit('selected', currentTime.value);
+    };
+
+    provide('currentKey', currentKey);
+    provide('currentTime', currentTime);
+    provide('muti', muti);
+
+    return {
+      classes,
+      popStyle,
+      close
+    };
+  }
+});
+</script>
+
+<style lang="scss" scoped>
+@import 'index.scss';
+</style>

+ 96 - 0
src/packages/__VUE/timeselect/index.vue

@@ -0,0 +1,96 @@
+<template>
+  <nut-popup
+    position="bottom"
+    closeable
+    round
+    :visible="visible"
+    :style="popStyle"
+    @click-overlay="close"
+    @click-close-icon="close"
+  >
+    <view :class="classes">
+      <view class="nut-timeselect__title">
+        <view class="nut-timeselect__title__fixed">
+          {{ title }}
+        </view>
+      </view>
+      <view class="nut-timeselect__content">
+        <view class="nut-timeselect__content__pannel">
+          <slot name="pannel"></slot>
+        </view>
+        <view class="nut-timeselect__content__detail">
+          <slot name="detail"></slot>
+        </view>
+      </view>
+    </view>
+  </nut-popup>
+</template>
+<script lang="ts">
+import { computed, provide } from 'vue';
+import { createComponent } from '../../utils/create';
+const { componentName, create } = createComponent('timeselect');
+export default create({
+  props: {
+    visible: {
+      type: Boolean,
+      defalut: false
+    },
+    height: {
+      type: [String],
+      default: '20%'
+    },
+    title: {
+      type: String,
+      default: '取件时间'
+    },
+    currentKey: {
+      type: [Number, String],
+      default: 0
+    },
+    currentTime: {
+      type: Array,
+      default: () => {
+        return [];
+      }
+    }
+  },
+  emits: ['update:visible', 'select'],
+  setup: (props: any, context: any) => {
+    const classes = computed(() => {
+      const prefixCls = componentName;
+      return {
+        [prefixCls]: true
+      };
+    });
+
+    const popStyle = computed(() => {
+      return {
+        width: '100%',
+        height: props.height
+      };
+    });
+
+    const currentKey = computed(() => props.currentKey);
+
+    const currentTime = computed(() => props.currentTime);
+
+    const close = () => {
+      context.emit('update:visible', false);
+      context.emit('select', currentTime.value);
+    };
+
+    provide('currentKey', currentKey);
+    provide('currentTime', currentTime);
+
+    return {
+      classes,
+      popStyle,
+      close
+    };
+  }
+});
+</script>
+
+<style lang="scss" scoped>
+@import 'index.scss';
+</style>

+ 21 - 0
src/packages/styles/variables.scss

@@ -286,5 +286,26 @@ $noticeBar-line-height: 24px !default;
 $noticeBar-left-icon-width: 16px !default;
 $noticeBar-right-icon-width: 16px !default;
 
+// TimeSelect
+$timeselect-title-font-size: $font-size-1 !default;
+$timeselect-title-color: $title-color !default;
+$timeselect-pannel-bg-color: #f6f7f9 !default;
+
+// TimePannel
+$timepannel-text-color: $title-color2 !default;
+$timepannel-font-size: $font-size-2 !default;
+$timepannel-cur-bg-color: $white !default;
+$timepannel-cur-text-color: #333333 !default;
+// TimeDetail
+$timedetail-item-bg-color: #f6f7f9 !default;
+$timedetail-item-border-radius: 5px !default;
+$timedetail-item-text-color: #333333 !default;
+$timedetail-item-text-font-size: $font-size-2 !default;
+$timedetail-item-cur-bg-color: #fef4f3 !default;
+$timedetail-item-cur-border: #f0250f !default;
+$timedetail-item-cur-text-color: #f0250f !default;
+$timedetail-time-text-color: #999 !default;
+$timedetail-time-font-size: $font-size-1 !default;
+
 @import './mixins/index';
 @import './animation/index';

+ 2 - 1
src/sites/mobile-taro/vue/src/app.config.ts

@@ -73,7 +73,8 @@ export default {
       pages: [
         'pages/address/index',
         'pages/signature/index',
-        'pages/barrage/index'
+        'pages/barrage/index',
+        'pages/timeselect/index'
       ]
     }
   ],

+ 3 - 0
src/sites/mobile-taro/vue/src/business/pages/timeselect/index.config.js

@@ -0,0 +1,3 @@
+export default {
+  navigationBarTitleText: 'TimeSelect'
+};

+ 197 - 0
src/sites/mobile-taro/vue/src/business/pages/timeselect/index.vue

@@ -0,0 +1,197 @@
+<template>
+  <div class="demo">
+    <h2>基本用法</h2>
+    <nut-cell @click="handleClick1">
+      <span><label>请选择配送时间</label></span>
+    </nut-cell>
+    <nut-timeselect
+      v-model:visible="visible1"
+      height="50%"
+      :current-key="currentKey1"
+      :current-time="currentTime1"
+      @selected="handleSelected1"
+    >
+      <template #pannel>
+        <nut-timepannel
+          name="2月23日(今天)"
+          pannel-key="0"
+          @change="handleChange1"
+        ></nut-timepannel>
+        <nut-timepannel
+          name="2月24日(星期三)"
+          pannel-key="1"
+          @change="handleChange1"
+        ></nut-timepannel>
+      </template>
+      <template #detail>
+        <nut-timedetail :times="times1" @select="selectTime1"></nut-timedetail>
+      </template>
+    </nut-timeselect>
+    <h2>可选择多个日期时间</h2>
+    <nut-cell @click="handleClick2">
+      <span><label>请选择配送时间</label></span>
+    </nut-cell>
+    <nut-timeselect
+      v-model:visible="visible2"
+      height="50%"
+      :current-key="currentKey2"
+      :current-time="currentTime2"
+      @selected="handleSelected2"
+    >
+      <template #pannel>
+        <nut-timepannel
+          name="2月23日(今天)"
+          pannel-key="0"
+          @change="handleChange2"
+        ></nut-timepannel>
+        <nut-timepannel
+          name="2月24日(星期三)"
+          pannel-key="1"
+          @change="handleChange2"
+        ></nut-timepannel>
+      </template>
+      <template #detail>
+        <nut-timedetail :times="times2" @select="selectTime2"></nut-timedetail>
+      </template>
+    </nut-timeselect>
+    <nut-toast :msg="msg" v-model:visible="show" :type="type" :cover="cover" />
+  </div>
+</template>
+
+<script lang="ts">
+import { reactive, toRefs, onMounted, defineComponent } from 'vue';
+export default defineComponent({
+  setup() {
+    const state = reactive({
+      visible1: false,
+      currentKey1: 0,
+      currentTime1: [] as any[],
+      times1: [
+        {
+          key: 0,
+          list: ['9:00-10:00', '10:00-11:00', '11:00-12:00']
+        },
+        {
+          key: 1,
+          list: ['9:00-10:00', '10:00-11:00']
+        }
+      ],
+      visible2: false,
+      currentKey2: 0,
+      currentTime2: [] as any[],
+      times2: [
+        {
+          key: 0,
+          list: ['9:00-10:00', '10:00-11:00', '11:00-12:00']
+        },
+        {
+          key: 1,
+          list: ['9:00-10:00', '10:00-11:00']
+        }
+      ]
+    });
+
+    const toastState = reactive({
+      msg: 'toast',
+      type: 'text',
+      show: false,
+      cover: false
+    });
+
+    const handleChange1 = (pannelKey: number) => {
+      state.currentKey1 = pannelKey;
+      state.currentTime1 = [];
+      state.currentTime1.push({
+        key: state.currentKey1,
+        list: []
+      });
+    };
+
+    const handleClick1 = () => {
+      state.visible1 = true;
+    };
+
+    const selectTime1 = (item: string) => {
+      let curTimeIndex = state.currentTime1[0]['list'].findIndex(
+        (time: string) => time === item
+      );
+      if (curTimeIndex === -1) {
+        state.currentTime1[0]['list'].push(item);
+      } else {
+        state.currentTime1[0]['list'].splice(curTimeIndex, 1);
+      }
+    };
+
+    const handleSelected1 = (obj: any) => {
+      toastState.show = true;
+      toastState.msg = JSON.stringify(obj);
+      toastState.type = 'text';
+      toastState.cover = false;
+    };
+
+    const handleChange2 = (pannelKey: number) => {
+      state.currentKey2 = pannelKey;
+      let curTime = state.currentTime2.find(
+        (item: any) => item.key == pannelKey
+      );
+      if (!curTime) {
+        state.currentTime2.push({
+          key: pannelKey,
+          list: []
+        });
+      }
+    };
+
+    const handleClick2 = () => {
+      state.visible2 = true;
+    };
+
+    const selectTime2 = (item: string) => {
+      let findIndex = state.currentTime2.findIndex(
+        (item: any) => item.key == state.currentKey2
+      );
+      let curTimeIndex = state.currentTime2[findIndex]['list'].findIndex(
+        (time: string) => time === item
+      );
+      if (curTimeIndex === -1) {
+        state.currentTime2[findIndex]['list'].push(item);
+      } else {
+        state.currentTime2[findIndex]['list'].splice(curTimeIndex, 1);
+      }
+    };
+
+    const handleSelected2 = (obj: any) => {
+      toastState.show = true;
+      toastState.msg = JSON.stringify(obj);
+      toastState.type = 'text';
+      toastState.cover = false;
+    };
+
+    onMounted(() => {
+      state.currentTime1.push({
+        key: state.currentKey1,
+        list: []
+      });
+      state.currentTime2.push({
+        key: state.currentKey2,
+        list: []
+      });
+    });
+
+    return {
+      ...toRefs(state),
+      ...toRefs(toastState),
+      handleChange1,
+      handleSelected1,
+      selectTime1,
+      handleClick1,
+      handleChange2,
+      handleSelected2,
+      selectTime2,
+      handleClick2
+    };
+  }
+});
+</script>
+
+<style lang="scss"></style>