基于quasar,只选择年度与月份的组件

为什么要做

quasar是个基于vue的强大的UI开发库,它提供了非常多的组件,比如日期选择。但是有些时候只需要选择到月份就可以了,quasar中没有,所以自己动手写了一个。因为对界面编程我不熟悉,所以,如果你有更好的想法,请不吝赐教。源码在gitcode、gitee、github的至简网格/企业服务上都有,以Apache License协议开源,gitcode连接如下:

assets/file/v3/components/month_input.js · master · 至简网格 / 企业服务 · GitCode

用法:

1)先引入

import MonthInput from "path_to_components/month_input.js"

2)再注册

组件中注册:components:{"month-input":MonthInput}

或全局注册:app.component('month-input', MonthInput);  app是使用Vue.createApp创建的。

3)最后使用

<month-input class="justify-center text-primary" min="-5" max="cur" @update:modelValue="f"></month-input>

属性

min、max、monthName(默认为“月”)

min、max接受以下几种格式:cur当前月份、-x当前年度减x年,+x当前年度加x年,yyyy/MM、yyyy-MM或yyyy.MM具体的月份;不设置则不限制。

update:modelValue触发回调时传递一个对象,其中包括{year:年份,month:月份(从1开始),num:年份*12+月份-1}

操作

没有点击时显示如下:

点击左右箭头前移一个月或后移一个月;点击中间的年/月,显示一个弹窗,在min、max指定范围之外的年月显示为灰色,如果选中的年份不在当前年份列表页中,月份全部显示灰色。点击左右箭头前移或后移12年。先选择年份,然后选择月份就可以了触发update:modelValue,同时隐藏弹窗。

源码

//月份选择组件month-input
export default{
data(){return {
    maxMonth:0,
    minMonth:0,
    year:'',
    month:'',
    num:0,//year*12+month-1
    startYear:0,
    years:[],
    months:[],
    old:{y:0,m:0}
}},
props: {
    modelValue:{type:String},
    min:{type:String,default:''}, //年份
    max:{type:String,default:''},
    monthName:{type:String, default:"月"}
},
emits: ['update:modelValue'],
created(){
    var m=this.parse(this.max,{y:9999,m:1});
    this.maxMonth=m.y*12+m.m-1;
    m=this.parse(this.min,{y:0,m:1});
    this.minMonth=m.y*12+m.m-1;
    var dt=new Date();
    m=this.parse(this.modelValue,{y:dt.getFullYear(),m:dt.getMonth()+1})
    this.old={year:m.y, month:m.m};
    this.year=m.y;
    this.month=m.m;
    this.num=this.year*12+this.month-1;
    this.startYear=this.year-4;
    this.set_array();
},
methods:{
set_array() {
    var month;
    var list=[];
    var chked=false;
    var v=this.startYear;
    var yMax=Math.floor(this.maxMonth/12);
    var yMin=Math.floor(this.minMonth/12);

    for(var i=0;i<4;i++) {
        var yy=[];
        for(var j=0;j<3;j++) {
            var y={v:v,cls:"col text-center"};
            if(v>yMax||v<yMin) {
                y.cls+=" text-grey";
            } else if(v==this.year) {
                y.cls+=" bg-primary text-white";
                chked=true;
            }
            yy.push(y);
            v++;
        }
        list.push(yy);
    }
    this.years=list;
    
    v=1;
    list=[];
    for(var i=0;i<4;i++) {
        var mm=[];
        for(var j=0;j<3;j++) {
            var m={v:v,cls:"col text-center"};
            if(chked) {//所选年份可见
                month=this.year*12+v-1;
                if(month>this.maxMonth||month<this.minMonth) {
                    m.cls+=" text-grey";
                } else if(v==this.month) {
                    m.cls+=" bg-primary text-white";
                }
            } else {
                m.cls+=" text-grey";
            }
            mm.push(m);
            v++;
        }
        list.push(mm);
    }    
    this.months=list;
},
parse(s,def) {
    if(!s) {
        return def;
    }
    var cfg=s.trim().toLowerCase();
    var dt=new Date();
    var y=dt.getFullYear();
    var m=dt.getMonth()+1;
    if(cfg=="cur") {
        return {y:y,m:m};
    }
    if(cfg.startsWith('-')) {
        var v = parseInt(cfg.substring(1));
        return {y:y-v, m:m};
    }
    if(cfg.startsWith('+')) {
        var v = parseInt(cfg.substring(1));
        return {y:y+v, m:m};
    }
    var ss=cfg.split(/[\/,-,.]+/)
    if(ss.length>0) y=parseInt(ss[0]);
    if(ss.length>1) m=parseInt(ss[1]);
    return {y:y,m:m};
},
pageUp() {
    this.startYear-=12;
    this.set_array();
},
pageDn() {
    this.startYear+=12;
    this.set_array();
},
fore(){
    if(this.num<=this.minMonth)return;
    
    this.month--;
    if(this.month==0) {
        this.year--;
        this.month=12;
    }
    this.num=this.year*12+this.month-1;
    this.old={year:this.year, month:this.month,num:this.num};
    this.$emit('update:modelValue', this.old);
},
next(){
    if(this.num>=this.maxMonth)return;
    this.month++;
    if(this.month>12) {
        this.year++;
        this.month=1;
    }
    this.num=this.year*12+this.month-1;
    this.old={year:this.year, month:this.month,num:this.num};
    this.$emit('update:modelValue', this.old);
},
set_year(v) {
    var yMax=Math.ceil(this.maxMonth/12);
    var yMin=Math.floor(this.minMonth/12);
    if(v>yMax||v<yMin) {
        return;
    }
    this.month=1;
    this.year=v;
    this.set_array();
},
set_month(v) {
    if(this.year<this.startYear||this.year>=this.startYear+12) {
        return;//未选择年份
    }
    var m=v-1+this.year*12;
    if(m>this.maxMonth||m<this.minMonth) {
        return; //不在有效范围内
    }
    this.month=v;
    if(v!=this.old.month||this.year!=this.old.year) {
        this.num=this.year*12+this.month-1;
        this.old={year:this.year, month:this.month,num:this.num};
        this.$emit('update:modelValue',this.old);
    }
    this.$refs._my_dlg.hide();
}
},
computed: {
   value: {
      get() {return this.year+'/'+(this.month>9?this.month:('0'+this.month))},
      set(v) {
          var ym=this.parse(v);
          this.year=ym.y;
          this.month=ym.m;
          this.num=this.year*12+this.month-1;
      }
   }
},
template: `
<div class="row">
 <div class="rol q-pr-lg">
  <q-icon name="navigate_before" @click="fore"
   :class="num<=minMonth?'text-grey':''"></q-icon>
 </div>
 <div class="rol" style="cursor:pointer">
 {{year}} / {{month<10?('0'+month):month}}
 <q-popup-proxy cover @before-show="set_array" ref="_my_dlg">
  <div class="row q-pa-sm" style="min-width:20em;">
   <div class="col self-center">
    <q-icon name="navigate_before" @click="pageUp" size="2em" class="q-px-sm"></q-icon>
   </div>
   <div class="col-8">
    <div class="row" v-for="yy in years">
     <div v-for="y in yy" :class="y.cls" @click="set_year(y.v)"
      style="cursor:pointer">{{y.v}}</div>
    </div>
    <q-separator spaced="md"></q-separator>
    <div class="row" v-for="mm in months">
     <div v-for="m in mm" :class="m.cls" @click="set_month(m.v)"
     style="cursor:pointer">{{m.v}}{{monthName}}</div>
    </div>
   </div>
   <div class="col self-center">
    <q-icon name="navigate_next" @click="pageDn" size="2em" class="q-px-sm"></q-icon>
   </div>
  </div>
 </q-popup-proxy>
 </div>
 <div class="rol q-pl-lg">
  <q-icon name="navigate_next" @click="next"
  :class="num>=maxMonth?'text-grey':''"></q-icon>
 </div> 
</div>
`
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值