Stories

Detail Return Return

記錄---基於uniapp,編寫一個自定義的日期組件 - Stories Detail

🧑‍💻 寫在開頭

點贊 + 收藏 === 學會🤣🤣🤣

環境配置

系統:windows10

平台:HBuilderX4.76

語言:vue、javascript

庫:uni

概述

本文是基於uniapp,編寫的自定義日期選擇器組件,大致效果如下:

企業微信截圖_20250905162025

1、組件簡介

這是一個日期選擇器,即可以選擇年、月、日的組件,所以,組件的功能是很簡單的,組件的效果就是如上面的效果圖所示,按照年月日基本劃分為三個區域。

一般的日期組件,都是不列出詳細年份與月份的,只會顯示月份下面詳細日期。如果要修改年份或者月份,通常是再次點擊,在彈出的窗口滑動或者滾動選擇。 比如uni提供的日期選擇器:

企業微信截圖_20250905162035

 

企業微信截圖_20250905162044

然,以上舉例不涉及孰優孰劣,只涉及個人喜好。

我個人的想法是,如果一個要選擇出生年月日的組件,為什麼不直接讓用户能夠在一個頁面選擇呢?而不是還需要在二次界面上去滾動選擇。

至少像月份,就12個月,是完全可以列出來的。

而年份也是可以的,但是對於不同人的出生年份,初始化時肯定無法兼容,所以增加了一個輸入框,可以直接輸入需要的年份,或者輸入一個接近的年份,輸入完成後,年份列表會顯示前後10年(範圍值可以設置,默認是10)的所有年份。

2、組件實現

如果你也喜歡這樣的日期選擇界面,可以接着往下看,下面將介紹一下組件的實現。

我們使用HBuilder新建一個項目,然後添加一個component文件夾,在文件夾下添加一個頁面:myDatePicker.vue:

企業微信截圖_20250905162054

我們主要的代碼都是寫在這個文件中。

組件的結構,是兩個部分,一個是固定顯示部分,一個是彈出窗口。 固定顯示部分就是我們在其他頁面調用組件時顯示所有,點擊此部分,就會彈出日期選擇界面。

所以,彈出界面在初始化時,是隱藏的。點擊固定顯示部分,則彈出選擇界面,如果點擊組件外部的任何空白部分,則彈出界面隱藏。

上面所描述的,也都是最基本的功能了。

我們按照年、月、日三個部分來分別説一下實現。

1、年份

如圖,我們自定義的組件中,年份是按照列表來顯示的,它是當前選定年份為中心,前、後各顯示10年的範圍,其中10年是一個參數range,可以設置,也可以使用默認(為10)。

企業微信截圖_20250905162102

 如上圖,我們將range定義為屬性,然後我們添加一個年份列表,使用計算屬性:

const yearList = computed(()=>{
		const centerYear = selectedYear.value;
		let start =centerYear - props.range;
		let end = centerYear + props.range;
		let tempCenterYear = centerYear;
		//對起始年份進行判斷
		if(start < 1900){
			tempCenterYear = 1900 + props.range;
			start = 1900;
			end = tempCenterYear + props.range;
		}
		//生成年份列表
		const list =Array.from(
			{length:end - start + 1},
			(_,i)=>start + i
		); 
		if(!list.includes(currentYear.value)){
			list.push(currentYear.value)
		}
		return list;
	});
2、月份

月份不需要計算,固定為12個月即可。

3、日

每個月的日期,則需要根據月份進行計算,比如2月一般只有28天,但是需要計算年份是否是閏年,如果是閏年,則為29天。至於大小月之分,則比較簡單,固定值即可。

const daysInMonth = computed(()=>{
		const year = tempselectedYear.value;
		const mon = tempselectedMonth.value;
		/* const mon = selectedMonth.value; */
		if(mon === 2) {
			//判斷年份是否是閏年
			const isLeapYear = (year % 4 === 0 && year % 100 !==0) || (year % 400 === 0);
			return isLeapYear ? 29:28;
		};
		if([4,6,9,11].includes(mon))return 30;
		return 31;
	});
4、星期對應

通常,一個日期選擇器都會將每個月的每一天對應星期幾都列出來,如下:

企業微信截圖_20250905162113

所以,每個月的天數顯示,都不是隻顯示當月的天數,有時候會包含上個月末尾幾天以及下個月開頭幾天。

這取決於所選擇的月份的1號是星期幾,比如,1號是星期三,如果星期的排布從左到右是週日到週六,那麼應該將上個月的最後幾天填充進來,以便於日期顯示有一種連續感。

所以,這裏就涉及到一個計算,即,當選擇某個月份後,怎樣判斷上個月的末尾顯示幾天,下個月的開頭顯示幾天,而這又取決於當月1號是星期幾。 所以,我們可以添加兩個計算屬性:

//計算上個月需要補幾天
	const prevMonthDays = computed(()=>{
		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>=0;i--){
			days.push(
				{
					day:prevMonthLastDay - i,
					month:prevMonth,
					year:prevYear,
					type:'prev'
				}
			);
		};
		return days;
	});
//計算下個月開頭幾天
	const nextMonthDays = computed(()=>{
		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<=daystoshow;i++){
			days.push({
				day:i,
				month:nextMonth,
				year:nextMonthYear,
				type:'next'
			});
		};
		return days;
	});

如上,其中最重要的是前一個月末尾幾天的計算,這個計算出來,下一個月的天數就好計算了。因為我們為了方便,整個天數顯示界面是設置了一個總數即42天,也就是6個星期。其中包含了當月所有天數,上個月末尾天數,以及下個月開頭天數。

最後,我們把所有天數彙總:

	//合併日期
	const allDays = computed(()=>{
		let alldays = [];
		alldays =  [
			...prevMonthDays.value,
			...Array.from({length:daysInMonth.value},(_,i)=>({
				day:i+1,
				month:tempselectedMonth.value,
				year:selectedYear.value,
				type:'select'
			})),
			...nextMonthDays.value
		];
		return alldays;
	});

然後我們渲染天數使用allDays變量即可:

<view class="popup-day">
					<view
						v-for="dd in allDays"
						:key="dd"
						class="popup-day1"
						:class="[
							{ 'active':tempselectedDay === dd.day && dd.type === 'select' },
							{'current-day':dd.day === currentDay && dd.type !== 'prev' && dd.type !== 'next' && tempselectedDay !== dd.day},
							{'noncurrent-day':dd.type === 'prev' || dd.type === 'next'}
							]"
						@click="dd.type === 'select' && day1Click(dd.day)">
						<text>{{dd.day}}</text>
					</view>
				</view>

渲染效果如下圖:

企業微信截圖_20250905162122

 如圖,樣式上,只有當月天數可以選擇,前月和後月天數只能看,不能選,因此為灰色。而當月天數,如果選中,則背景色為藍色。而對於今日日期,如果選中,則也是藍色背景,如果選擇了其他天數,則今天會顯示一個虛線橙色外框,以提示用户。

企業微信截圖_20250905162130

 上面就是大致的思路,最後完成的效果演示:

8514a5d4da584b369a27718a095a6aa9~tplv-73owjymdk6-jj-mark-v1_0_0_0_0_5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65p6E5biI_q75

注:此組件已經發布到uni插件市場,感興趣的可以下載測試(免費):

日期選擇器-自定義組件-DatePicker - DCloud 插件市場

 

本文轉載於:https://juejin.cn/post/7546051261582278698

如果對您有所幫助,歡迎您點個關注,我會定時更新技術文檔,大家一起討論學習,一起進步。

user avatar linlinma Avatar shuirong1997 Avatar nznznz Avatar 79px Avatar aresn Avatar chongdongdedaxiongmao_kxfei Avatar
Favorites 6 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.