大孔雀 發表於 2025-9-5 16:32:00

记录---基于uniapp,编写一个自定义的日期组件

<h1 data-id="heading-0">🧑‍💻 写在开头</h1>
<p>点赞 + 收藏 === 学会🤣🤣🤣</p>
<h4 data-id="heading-1">环境配置</h4>
<blockquote>
<p>系统:windows10</p>
<p>平台:HBuilderX4.76</p>
<p>语言:vue、javascript</p>
<p>库:uni</p>
</blockquote>
<h4 data-id="heading-2">概述</h4>
<blockquote>
<p>本文是基于uniapp,编写的自定义日期选择器组件,大致效果如下:</p>
</blockquote>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202509/2149129-20250905162729057-1562304239.png" alt="企业微信截图_20250905162025" loading="lazy"></p>
<h4 data-id="heading-3">1、组件简介</h4>
<blockquote>
<p>这是一个日期选择器,即可以选择年、月、日的组件,所以,组件的功能是很简单的,组件的效果就是如上面的效果图所示,按照年月日基本划分为三个区域。</p>
</blockquote>
<p>一般的日期组件,都是不列出详细年份与月份的,只会显示月份下面详细日期。如果要修改年份或者月份,通常是再次点击,在弹出的窗口滑动或者滚动选择。 比如uni提供的日期选择器:</p>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202509/2149129-20250905162748798-935642559.png" alt="企业微信截图_20250905162035" loading="lazy"></p>
<p>&nbsp;</p>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202509/2149129-20250905162803761-2136855281.png" alt="企业微信截图_20250905162044" loading="lazy"></p>
<p>然,以上举例不涉及孰优孰劣,只涉及个人喜好。</p>
<p>我个人的想法是,如果一个要选择出生年月日的组件,为什么不直接让用户能够在一个页面选择呢?而不是还需要在二次界面上去滚动选择。</p>
<p>至少像月份,就12个月,是完全可以列出来的。</p>
<p>而年份也是可以的,但是对于不同人的出生年份,初始化时肯定无法兼容,所以增加了一个输入框,可以直接输入需要的年份,或者输入一个接近的年份,输入完成后,年份列表会显示前后10年(范围值可以设置,默认是10)的所有年份。</p>
<h4 data-id="heading-4">2、组件实现</h4>
<p>如果你也喜欢这样的日期选择界面,可以接着往下看,下面将介绍一下组件的实现。</p>
<p>我们使用HBuilder新建一个项目,然后添加一个<code>component</code>文件夹,在文件夹下添加一个页面:<code>myDatePicker.vue</code>:</p>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202509/2149129-20250905162831664-617072187.png" alt="企业微信截图_20250905162054" loading="lazy"></p>
<p>我们主要的代码都是写在这个文件中。</p>
<p>组件的结构,是两个部分,一个是固定显示部分,一个是弹出窗口。 固定显示部分就是我们在其他页面调用组件时显示所有,点击此部分,就会弹出日期选择界面。</p>
<p>所以,弹出界面在初始化时,是隐藏的。点击固定显示部分,则弹出选择界面,如果点击组件外部的任何空白部分,则弹出界面隐藏。</p>
<p>上面所描述的,也都是最基本的功能了。</p>
<p>我们按照年、月、日三个部分来分别说一下实现。</p>
<h6 data-id="heading-5">1、年份</h6>
<p>如图,我们自定义的组件中,年份是按照列表来显示的,它是当前选定年份为中心,前、后各显示10年的范围,其中10年是一个参数range,可以设置,也可以使用默认(为10)。</p>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202509/2149129-20250905162844347-1683627456.png" alt="企业微信截图_20250905162102" loading="lazy"></p>
<p>&nbsp;如上图,我们将range定义为属性,然后我们添加一个年份列表,使用计算属性:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">const yearList = computed(()=&gt;{
                const centerYear = selectedYear.value;
                let start =centerYear - props.range;
                let end = centerYear + props.range;
                let tempCenterYear = centerYear;
                //对起始年份进行判断
                if(start &lt; 1900){
                        tempCenterYear = 1900 + props.range;
                        start = 1900;
                        end = tempCenterYear + props.range;
                }
                //生成年份列表
                const list =Array.from(
                        {length:end - start + 1},
                        (_,i)=&gt;start + i
                );
                if(!list.includes(currentYear.value)){
                        list.push(currentYear.value)
                }
                return list;
        });</pre>
</div>
<h6 data-id="heading-6">2、月份</h6>
<p>月份不需要计算,固定为12个月即可。</p>
<h6 data-id="heading-7">3、日</h6>
<p>每个月的日期,则需要根据月份进行计算,比如2月一般只有28天,但是需要计算年份是否是闰年,如果是闰年,则为29天。至于大小月之分,则比较简单,固定值即可。</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">const daysInMonth = computed(()=&gt;{
                const year = tempselectedYear.value;
                const mon = tempselectedMonth.value;
                /* const mon = selectedMonth.value; */
                if(mon === 2) {
                        //判断年份是否是闰年
                        const isLeapYear = (year % 4 === 0 &amp;&amp; year % 100 !==0) || (year % 400 === 0);
                        return isLeapYear ? 29:28;
                };
                if(.includes(mon))return 30;
                return 31;
        });</pre>
</div>
<h6 data-id="heading-8">4、星期对应</h6>
<p>通常,一个日期选择器都会将每个月的每一天对应星期几都列出来,如下:</p>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202509/2149129-20250905162926015-154426156.png" alt="企业微信截图_20250905162113" loading="lazy"></p>
<p>所以,每个月的天数显示,都不是只显示当月的天数,有时候会包含上个月末尾几天以及下个月开头几天。</p>
<p>这取决于所选择的月份的1号是星期几,比如,1号是星期三,如果星期的排布从左到右是周日到周六,那么应该将上个月的最后几天填充进来,以便于日期显示有一种连续感。</p>
<p>所以,这里就涉及到一个计算,即,当选择某个月份后,怎样判断上个月的末尾显示几天,下个月的开头显示几天,而这又取决于当月1号是星期几。 所以,我们可以添加两个计算属性:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">//计算上个月需要补几天
        const prevMonthDays = computed(()=&gt;{
                const prevYear = tempselectedMonth.value === 1 ? tempselectedYear.value - 1 : tempselectedYear.value;
                const prevMonth = tempselectedMonth.value === 1 ? 12 : tempselectedMonth.value - 1;
                //获取上个月的最后一天是几号
                //console.log(`${prevYear}--${prevMonth}`);
                const prevMonthLastDay = new Date(prevYear,prevMonth,0).getDate();
                //console.log(prevMonthLastDay);
                const cur_weekday = new Date(tempselectedYear.value,tempselectedMonth.value-1,1).getDay();
                //console.log(cur_weekday)
                const days = [];
                for(let i = cur_weekday - 1;i&gt;=0;i--){
                        days.push(
                                {
                                        day:prevMonthLastDay - i,
                                        month:prevMonth,
                                        year:prevYear,
                                        type:'prev'
                                }
                        );
                };
                return days;
        });</pre>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">//计算下个月开头几天
        const nextMonthDays = computed(()=&gt;{
                const nextMonthYear = tempselectedMonth.value === 12 ? tempselectedYear.value + 1 : tempselectedYear.value;
                const nextMonth = tempselectedMonth.value === 12 ? 1 : tempselectedMonth.value + 1;
                const total = 42;
                const daystoshow = total - (prevMonthDays.value.length + daysInMonth.value);
                const days = [];
                for(let i=1;i&lt;=daystoshow;i++){
                        days.push({
                                day:i,
                                month:nextMonth,
                                year:nextMonthYear,
                                type:'next'
                        });
                };
                return days;
        });</pre>
</div>
<p>如上,其中最重要的是前一个月末尾几天的计算,这个计算出来,下一个月的天数就好计算了。因为我们为了方便,整个天数显示界面是设置了一个总数即42天,也就是6个星期。其中包含了当月所有天数,上个月末尾天数,以及下个月开头天数。</p>
<p>最后,我们把所有天数汇总:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">        //合并日期
        const allDays = computed(()=&gt;{
                let alldays = [];
                alldays =[
                        ...prevMonthDays.value,
                        ...Array.from({length:daysInMonth.value},(_,i)=&gt;({
                                day:i+1,
                                month:tempselectedMonth.value,
                                year:selectedYear.value,
                                type:'select'
                        })),
                        ...nextMonthDays.value
                ];
                return alldays;
        });</pre>
</div>
<p>然后我们渲染天数使用allDays变量即可:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">&lt;view class="popup-day"&gt;
                                        &lt;view
                                                v-for="dd in allDays"
                                                :key="dd"
                                                class="popup-day1"
                                                :class="[
                                                        { 'active':tempselectedDay === dd.day &amp;&amp; dd.type === 'select' },
                                                        {'current-day':dd.day === currentDay &amp;&amp; dd.type !== 'prev' &amp;&amp; dd.type !== 'next' &amp;&amp; tempselectedDay !== dd.day},
                                                        {'noncurrent-day':dd.type === 'prev' || dd.type === 'next'}
                                                        ]"
                                                @click="dd.type === 'select' &amp;&amp; day1Click(dd.day)"&gt;
                                                &lt;text&gt;{{dd.day}}&lt;/text&gt;
                                        &lt;/view&gt;
                                &lt;/view&gt;</pre>
</div>
<p>渲染效果如下图:</p>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202509/2149129-20250905163024902-2057533807.png" alt="企业微信截图_20250905162122" loading="lazy"></p>
<p>&nbsp;如图,样式上,只有当月天数可以选择,前月和后月天数只能看,不能选,因此为灰色。而当月天数,如果选中,则背景色为蓝色。而对于今日日期,如果选中,则也是蓝色背景,如果选择了其他天数,则今天会显示一个虚线橙色外框,以提示用户。</p>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202509/2149129-20250905163038102-280196741.png" alt="企业微信截图_20250905162130" loading="lazy"></p>
<p>&nbsp;上面就是大致的思路,最后完成的效果演示:</p>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202509/2149129-20250905163052476-1862876523.webp" alt="8514a5d4da584b369a27718a095a6aa9~tplv-73owjymdk6-jj-mark-v1_0_0_0_0_5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65p6E5biI_q75" loading="lazy"></p>
<p>注:此组件已经发布到uni插件市场,感兴趣的可以下载测试(免费):</p>
<p>日期选择器-自定义组件-DatePicker - DCloud 插件市场</p>
<p>&nbsp;</p>
<div>
<h2>本文转载于:https://juejin.cn/post/7546051261582278698</h2>
</div>
<h3 id="tid-D8HBxE">如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。</h3>
<p><em><img src="https://img2024.cnblogs.com/blog/2149129/202501/2149129-20250122165814748-630765389.png" alt="" loading="lazy"></em></p><br><br>
来源:https://www.cnblogs.com/smileZAZ/p/19075648
頁: [1]
查看完整版本: 记录---基于uniapp,编写一个自定义的日期组件