自定义 “至今”选项日期选择器

  • 实现缘由

有这么一个分享的原因是,我发现很多选择日期的地方都会要求能选择“至今”,但是现在广泛使用的UI库中,都是不支持的。很奇怪,这个需求还是挺常见的,为什么都不支持呢?网上类似的控件也很难找,几乎没找到。所以我自定义了这么一款控件,基于vant picker 来实现的,但是,如果想依赖于其他的UI库,也是比较好迁移过去实现的。

  • 效果图

 

  • 代码分享

<template>
    <div class="container">
        <van-popup v-model="isShow" position="bottom" @close="onClosePop">
            <van-picker
                ref="van_picker"
                :columns="columns"
                show-toolbar
                :title="pickerShowTitle"
                @confirm="onConfirm"
                @cancel="onCancle"
                @change="onChange"
            />
        </van-popup>
    </div>
</template>
<script lang="ts">
import {Component, Prop, Vue, Watch} from 'vue-property-decorator'
 @Component({
    name: 'CustomDate'
})
export default class CustomDate extends Vue {
    public $refs!: {
        van_picker: HTMLFormElement;
    };
    @Prop({
        type: Boolean,
        default: false
    })
    isShowProp!: boolean;
    @Prop({
        type: Boolean,
        default: false
    })
    isMultiProp!: boolean;
    @Prop({
        type: String,
        default: ''
    })
    showTimeProp!: string;
    @Prop({
        type: String,
        default: ''
    })
    titleProp!: string;
    columns: any = [];
    isShow: boolean = this.isShowProp;
    isMulti: boolean = this.isMultiProp;
    showTime = this.showTimeProp;
    pickerTitle = this.titleProp.split('-');
    pickerShowTitle = '';
    multiShowTime = '';
    @Watch('isShowProp', {
        deep: true
    })
    watchisShow(data: any) {
        this.isShow = data;
        this.$nextTick(()=>{
            this.pickerShowTitle = this.pickerTitle[0];
            this.showTime = this.showTimeProp;
            this.timeInit();
        })
    }
    initLastData() {
        const myDate = new Date();
        const data = {
            year: myDate.getFullYear(),
            month: myDate.getMonth() + 1,
            date: myDate.getDate(),
        };
        const options1 = [{ text: "至今", value: -1 }],options2 = [], options3 = [], dataFull = [];
        //初始化近30年
        for (let i = data.year; i > data.year - 31; i--) {
            options1.push({
                text: i + "",
                value: i,
            });
        }
        if (this.showTime !== '至今' && this.showTime !== '') {
            const arr = this.showTime.split('-')
            for (let j = 1; j <= data.month ; j++) {
                options2.push({
                    text: j < 10 ? "0" + j : "" + j,
                    value: j,
                });
            }
            // 非当前年
            for (let z = 1; z <= 12; z++) {
                dataFull.push({
                    text: z < 10 ? '0' + z : '' + z,
                    value: z < 10 ? '0' + z : '' + z
                })
            }
            // 初始化当前月天数数据,限制小于等于今天
            for (let j = 1; j <= data.date; j++) {
                options3.push({
                    text: j < 10 ? "0" + j : "" + j,
                    value: j,
                });
            }
            this.columns = [
                { values: options1 },
                { values: parseInt(arr[0]) < data.year ? dataFull : options2 },
                { values: options3 },
            ];
        } else {
            this.columns = [{values: options1}, {values: []},{values: []}];
        }
    }
    onConfirm(value: any) {
        let date: any = '';
        value.forEach((item: any, index: any) => {
            if(item) {
                date +=  index === 0 ? ''+ item.text : '-' + item.text;
            }
        });
        if (this.isMulti) {
            if (this.pickerShowTitle !== this.pickerTitle[1]) {// 第一次选择时间
                this.showTime = date;
                this.pickerShowTitle = this.pickerTitle[1];
                // 限制时间
                this.timeLimit(this.showTime)
            } else {// 第二次选择
                this.multiShowTime = this.showTime + '-' + date;
                this.$emit('onConfirm',this.multiShowTime);
            }
        } else {
            this.showTime = date;
            this.$emit('onConfirm',date)
        }
    }
    onCancle() {
        this.$emit('onCancle')
    }
    onClosePop() {
        this.$emit('onCancle')
    }
    // 限制选择起始时间后时间
    timeLimit(startTime: string) {
        const myDate = new Date()
        const data = {
            year: myDate.getFullYear()
        }
        const arr = startTime.split('-')
        const options1 = [{ text: "至今", value: -1 }], options2 = [], options3 = []
        for (let i = data.year; i >= parseInt(arr[0]); i--) {
            options1.push({
                text: i + "",
                value: i,
            });
        }
        for (let j = 12; j >= parseInt(arr[1]); j--) {
            options2.push({
                text: j < 10 ? '0' + j : '' + j,
                value: j
            })
        }
        const isLeapYear = parseInt(arr[0]) % 4 === 0 && parseInt(arr[0]) % 100 !== 0 || parseInt(arr[0]) % 400 === 0;
        const nowMonth =  isNaN(parseInt(arr[0])) ? -1 : parseInt(arr[1]);
        const isBigMonth = [1,3,5,7,8,10,12].indexOf(nowMonth) !== -1;
        for (let k = (isBigMonth? 31: (nowMonth === 2? (isLeapYear? 29:28) : 30)); k >= parseInt(arr[2]); k--) {
            options3.push({
                text: k < 10 ? '0' + k : '' + k,
                value: k
            })
        }
        this.$refs.van_picker.setColumnValues(0, options1)
        this.$refs.van_picker.setColumnValues(1, options2.reverse())
        this.$refs.van_picker.setColumnValues(2, options3.reverse())
        this.$refs.van_picker.setColumnValue(0, arr[0])
        this.$refs.van_picker.setColumnValue(1, arr[1])
        this.$refs.van_picker.setColumnValue(2, arr[2])
    }
    onChange(picker: any, values: any, index: any) {
        const myDate = new Date()
        const year = myDate.getFullYear()
        const month = myDate.getMonth() + 1
        const dataFull = [],options2 = [],options3 = []
        // 滑动年份数据
        if (index === 0) {
            const arr = this.showTime.split('-')
            if (this.isMulti && this.pickerShowTitle === this.pickerTitle[1] && parseInt(arr[0]) === values[0].value) {
                this.timeLimit(this.showTime)
                return;
            }
            for (let z = 1; z <= 12; z++) {
                dataFull.push({
                    text: z < 10 ? '0' + z : z,
                    value: z
                })
            }
            for (let j = 1; j <= month; j++) {
                options2.push({
                    text: j < 10 ? '0' + j : j,
                    value: j
                })
            }
            //如果选项变成至今,则月份选项为空
            if (values[0].value === -1) {
                picker.setColumnValues(1, [])
                picker.setColumnValues(2, [])
                return
            }
            //如果年选项小于当前年,则显示12个月
            if (values[0].value < year) {
                picker.setColumnValues(1, dataFull)
            } else {
                picker.setColumnValues(1, options2)
            }
            if (options3.length === 0) {
                const isLeapYear = values[0].value % 4 === 0 && values[0].value % 100 !== 0 || values[0].value % 400 === 0;
                const nowMonth = (values[0].value < year) ? dataFull[0].value : options2[0].value;
                const isBigMonth = [1,3,5,7,8,10,12].indexOf(nowMonth) !== -1;
                for (let j = 1; j <= (isBigMonth? 31: (nowMonth === 2? (isLeapYear? 29:28) : 30)); j++) {
                    options3.push({
                        text: j < 10 ? '0' + j : j,
                        value: j
                    })
                }
                picker.setColumnValues(2, options3)
            }
        } else if (index === 1) { // 滑动月份数据
            const arr = this.showTime.split('-')
            if (this.isMulti && this.pickerShowTitle === this.pickerTitle[1] && parseInt(arr[1]) === values[1].value) {
                this.timeLimit(this.showTime)
                return;
            }
            const isLeapYear = values[0].value % 4 === 0 && values[0].value % 100 !== 0 || values[0].value % 400 === 0;
            const isBigMonth = [1,3,5,7,8,10,12].indexOf(values[1].value) !== -1;
            for (let j = 1; j <= (isBigMonth? 31: (values[1].value === 2? (isLeapYear? 29:28) : 30)); j++) {
                options3.push({
                    text: j < 10 ? '0' + j : j,
                    value: j
                })
            }
            picker.setColumnValues(2, options3)
        }
    }
    timeInit(){
        const myDate = new Date()
        const data = {
            year: myDate.getFullYear(),
            month: myDate.getMonth() + 1
        }
        const arr = this.showTime.split('-')
        const options1 = [{ text: "至今", value: -1 }], dataFull = [], options2 = [], options3 = []
        if (this.showTime != '至今' && this.showTime !== '') {
            for (let i = data.year; i > data.year - 31; i--) {
                options1.push({
                    text: i + "",
                    value: i,
                });
            }
            // 12个月
            for (let z = 1; z <= 12; z++) {
                dataFull.push({
                    text: z < 10 ? '0' + z : '' + z,
                    value: z
                })
            }
            //小于当前月的选项
            for (let j = 1; j <= data.month; j++) {
                options2.push({
                    text: j < 10 ? '0' + j : '' + j,
                    value: j
                })
            }
            const isLeapYear = parseInt(arr[0]) % 4 === 0 && parseInt(arr[0]) % 100 !== 0 || parseInt(arr[0]) % 400 === 0;
            const nowMonth =  isNaN(parseInt(arr[0])) ? -1 : parseInt(arr[1]);
            const isBigMonth = [1,3,5,7,8,10,12].indexOf(nowMonth) !== -1;
            if (nowMonth !== -1) {
                for (let j = 1; j <= (isBigMonth? 31: (nowMonth === 2? (isLeapYear? 29:28) : 30)); j++) {
                    options3.push({
                        text: j < 10 ? '0' + j : j + '',
                        value: j
                    })
                }
            }
            this.$refs.van_picker.setColumnValues(0, options1)
            //回填月份数据
            if (parseInt(arr[0]) < data.year) {
                this.$refs.van_picker.setColumnValues(1, dataFull)
            } else {
                this.$refs.van_picker.setColumnValues(1, options2)
            }
            // 回填天数数据
            this.$refs.van_picker.setColumnValues(2, options3)
            //回填选中的值
            this.$refs.van_picker.setColumnValue(0, arr[0])
            this.$refs.van_picker.setColumnValue(1, arr[1])
            this.$refs.van_picker.setColumnValue(2, arr[2])
        } else {
            this.$refs.van_picker.setColumnIndex(0, 0, 0)
            this.$refs.van_picker.setColumnValues(1, [])
            this.$refs.van_picker.setColumnValues(2, [])
        }
    }
    mounted() {
        this.initLastData();
    }
}
</script>

<style lang="scss">
.container {
}
</style>

 

  • 如何使用

import CustomDate from "@/components/CustomDate.vue";
<CustomDate
      @onCancle="onCusDateCancle"
      @onConfirm="onCusDateConfirm"
      :isShowProp="isShowPop"
      :showTimeProp="showTimeProp"
      titleProp="起始时间-结束时间"
      isMultiProp>
 </CustomDate>


titleProp: 设置标题,以"-"分隔 实现区间选择的两个标题显示
isMutiprop: 设置是否开启区间选择

  • 实现的原理

主要是基于 vant picker 的 setColumnValues
来实现动态的日期显示,所以,原理其实挺简单,这里也是做一个分享,有需要的小伙伴可以拿来直接用,找不到合适的控件真的很难受。



这里感谢https://blog.csdn.net/weixin_40408338/article/details/105159765 提供了实现的思路,大家也可以看一下这位作者的文章,实现了年月的联动,比较清晰。

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值