uniapp、小程序自定义选择日、周、月、季、年的时间选择器组件

目录

代码分享

utils文件

uniapp使用插件使用

zdp-date-picker使用说明


本组件用到了uni-ui的uni-popup弹窗组件

废话不多说直接上代码

代码分享

<template>
	<view class="content-pop">
		<!-- <uni-popup ref="popup" type="bottom"> -->
		<view class="pop-cont" :class="showCont?'open':''">
			<view class="zdp-pop">
				<view class="top-cont">
					<view class="close-cont" @click="close(1)">
						取消
					</view>
					<!-- 可以自定义显示 -->
					<slot name="text" :scope="{range,zdpdate}">
						<view class="text-info">
							请选择时间
							<view>
								{{selInfo}}
							</view>
						</view>
					</slot>

					<view class="save-cont" @click="close(2)">
						确认
					</view>
				</view>
				<view class="tabs-cont">
					<view class="tab-list" v-for="item in tabs" :class="item.value==activeIdx?'active':''"
						@click="handleTabs(item.value)">
						{{item.label}}
					</view>
				</view>
				<view class="date-cont">
					<picker-view :value="value" @change="handleChange" class="picker-view">
						<picker-view-column v-for="obj in SelArr">
							<view class="item" v-for="(item,index) in obj.child" :key="index">
								{{item<10?'0'+item:item}}{{obj.value}}
							</view>
						</picker-view-column>
					</picker-view>
				</view>
			</view>
		</view>

		<!-- </uni-popup> -->
	</view>
</template>

<script>
	import {
		whichWeek
	} from '../../utils/getWeek.js'
	import {
		getMonthStartEnd
	} from '../../utils/getMonth.js'
	import {
		getQuarterDates
	} from '../../utils/getQuarter.js'
	export default {
		props: {
			type: {
				type: String,
				default: '日',
				validator(value, props) {
					return ['日', '月', '周', '年', '季'].includes(value)
				}
			},
			time: {
				type: String, //'2024-1-1' '2024-2' '2024-2' '2024-2' '2024

			}
		},
		data() {
			return {
				showCont: false,
				tabs: [{
						label: '日',
						value: '日'
					},
					{
						label: '周',
						value: '周',
					},
					{
						label: '月',
						value: '月',
					},
					{
						label: '季',
						value: '季',
					},
					{
						label: '年',
						value: '年',
					}
				],
				activeIdx: '日',
				value: [], //选择的值,要设置默认值
				dateArr: [],
				day: 18,
				week: 1,
				month: 1, //
				quarter: 2,
				years: 24,
				SelArr: [],
				range: '',
				zdpdate: ''
			}
		},
		computed: {
			selInfo() {
				let str = ''
				let {
					activeIdx,
					years,
					day,
					month,
					week,
					quarter
				} = this
				if (activeIdx == '日') {
					str = `20${years}年 ${month<10?'0'+month:month}月 ${day<10?'0'+day:day}日`
				} else if (activeIdx == '周') {
					str = `20${years}年 第${week}周`
				} else if (activeIdx == '月') {
					str = `20${years}年 ${month<10?'0'+month:month}月`
				} else if (activeIdx == '季') {
					str = `20${years}年 ${quarter}季`
				} else if (activeIdx == '年') {
					str = `20${years}年`
				}
				return str
			}
		},
		created() {

		},
		methods: {
			// 
			handleProps() {
				let arr = []
				arr = this.time.split('-')
				if (this.type == '日') {
					this.month = arr[1] - 0
					this.day = arr[2] - 0
				} else if (this.type == '周') {
					this.week = arr[1] - 0
				} else if (this.type == '季') {
					this.quarter = arr[1] - 0
				} else if (this.type == '月') {
					this.month = arr[1] - 0
					this.quarter = this.handleQuarter(this.month)
				}
				for (let i = 0; i < arr.length; i++) {
					if (i == 0) {
						arr[0] = arr[0].slice(2)
						arr[0] -= 0
						this.years = arr[0]
					} else {
						arr[i]--
					}
				}
				this.value = arr
			},
			// 初始化
			newDate() {
				// this.activeIdx=this.type
				const date = new Date()
				this.day = date.getDate()
				this.years = date.getFullYear()
				this.years = this.years.toString().slice(2, 4) - 0
				this.week = this.getYearWeek(date)
				this.month = date.getMonth() + 1
				this.quarter = this.handleQuarter(this.month)
				this.handleTabs(this.type)
				if (this.time) {
					this.handleProps()
					console.log(this.years);
				}
				this.initArr()
			},
			initArr() {
				const date = new Date()
				let arr = []
				let {
					activeIdx,
				} = this
				let day = []
				let week = []
				let month = []
				let quarter = []
				let years = []
				for (let i = 2000; i <= date.getFullYear(); i++) {
					years.push(i)
				}
				for (let i = 1; i <= 12; i++) {
					month.push(i)
				}
				let weekArr = whichWeek(`20${this.years}`)
				for (let i = 1; i <= weekArr.length; i++) {
					week.push(i)
				}
				for (let i = 1; i <= 4; i++) {
					quarter.push(i)
				}

				if (activeIdx == '日') {
					let num = this.getMonthDays(this.years, this.month)
					for (let i = 1; i <= num; i++) {
						day.push(i)
					}
					arr = [{
						value: "年",
						child: years
					}, {
						value: "月",
						child: month
					}, {
						value: "日",
						child: day
					}]
					this.zdpdate =
						`20${this.years}-${this.month<10?'0'+this.month:this.month}-${this.day<10?'0'+this.day:this.day}`
					this.range =
						`20${this.years}-${this.month<10?'0'+this.month:this.month}-${this.day<10?'0'+this.day:this.day}`
				} else if (activeIdx == '月') {
					arr = [{
						value: "年",
						child: years
					}, {
						value: "月",
						child: month
					}]
					// console.log(this.years,'年22');
					this.zdpdate = `20${this.years}-${this.month<10?'0'+this.month:this.month}`
					this.range = getMonthStartEnd(this.zdpdate)
				} else if (activeIdx == '年') {
					arr = [{
						value: "年",
						child: years
					}]
					this.zdpdate = `20${this.years}`
					this.range = `20${this.years}-1-31/20${this.years}-12-31`
				} else if (activeIdx == '周') {
					arr = [{
						value: "年",
						child: years
					}, {
						value: "周",
						child: week
					}]
					this.zdpdate = `20${this.years}年 第${this.week}周`
					let wk = weekArr[this.week - 1]
					this.range = `${wk.year}-${wk.month}-${wk.date}` + '/' +
						`${wk.last.year}-${wk.last.month}-${wk.last.date}`
				} else if (activeIdx == '季') {
					arr = [{
						value: "年",
						child: years
					}, {
						value: "季度",
						child: quarter
					}]
					this.zdpdate = `20${this.years}年 ${this.quarter}季度`
					let obj = getQuarterDates(`20${this.years}`, this.quarter)
					this.range = obj.startDate + '/' + obj.endDate
				}
				this.SelArr = arr
			},
			open() {
				this.newDate()
				// this.$refs.popup.open('bottom')
				this.showCont = true
			},
			close(num) {
				if (num == 1) {
					this.newDate()
					this.handleEmit()
				} else {
					this.handleEmit()
				}
				// this.$refs.popup.close()
				this.showCont = false
			},
			handleEmit() {
				let {
					zdpdate,
					range,
				} = this
				this.$emit('submit-date', {
					zdpdate,
					range,
					type: this.activeIdx
				})
			},

			handleTabs(val) {
				this.activeIdx = val
				let {
					activeIdx,
					years,
					month,
					day,
					quarter,
					week
				} = this
				if (activeIdx == '日') {
					this.value = [years, month - 1, day - 1]
				} else if (activeIdx == '周') {
					this.value = [years, week - 1]
				} else if (activeIdx == '月') {
					this.value = [years, month - 1]
				} else if (activeIdx == '季') {
					this.value = [years, quarter - 1, ]
				} else if (activeIdx == '年') {
					this.value = [years]
				}
				// console.log(this.value, '选择的日期');
				this.initArr()
			},
			// 选择器选择
			handleChange(e) {
				let {
					value
				} = e.detail
				let {
					activeIdx
				} = this
				// console.log(value);
				// 年小于前面+0
				if (value[0] < 10) {
					value[0] = '0' + value[0]
				}
				this.years = value[0]
				if (activeIdx == '日') {
					this.month = value[1] + 1 //月
					this.day = value[2] + 1 //日
				} else if (activeIdx == '周') {
					this.week = value[1] + 1 //周
				} else if (activeIdx == '季') {
					this.quarter = value[1] + 1
				} else if (activeIdx == '月') {
					this.month = value[1] + 1 //月
				}
				this.initArr()
			},
			//获取当前月的天数
			getMonthDays(year, month) {
				var thisDate = new Date(year, month, 0);
				return thisDate.getDate();
			},
			getYearWeek(endDate) {
				//本年的第一天
				let beginDate = new Date(endDate.getFullYear(), 0, 1);
				//星期从0-6,0代表星期天,6代表星期六
				let endWeek = endDate.getDay();
				if (endWeek == 0) endWeek = 7;
				let beginWeek = beginDate.getDay();
				if (beginWeek == 0) beginWeek = 7;
				//计算两个日期的天数差
				let millisDiff = endDate.getTime() - beginDate.getTime();
				let dayDiff = Math.floor((millisDiff + (beginWeek - endWeek) * (24 * 60 * 60 * 1000)) / 86400000);
				return Math.ceil(dayDiff / 7) + 1;
			},
			handleQuarter(month) {
				const oneQuarter = [1, 2, 3]
				const twoQuarter = [4, 5, 6]
				const threeQuarter = [7, 8, 9]
				let num
				if (oneQuarter.includes(month)) {
					num = 1
				} else if (twoQuarter.includes(month)) {
					num = 2
				} else if (threeQuarter.includes(month)) {
					num = 3
				} else {
					num = 4
				}
				return num
			}
		}
	}
</script>

<style lang="scss" scoped>
	.pop-cont {
		position: fixed;
		left: 0%;
		bottom: 0%;
		width: 100%;
		background-color: rgba(0, 0, 0, 0.5);
		height: 0;
		.zdp-pop{
			transform: translateY(100%);
		}
		
	}

	.pop-cont.open {
		height: 100%;
		.zdp-pop{
			transform: translateY(0);
		}
	}

	.zdp-pop {
		position: absolute;
		bottom: 0%;
		left: 0%;
		box-sizing: border-box;
		padding: 24rpx;
		width: 100%;
		height: 750rpx;
		background-color: #fff;
		transition: all 1s; 
		.top-cont {
			height: 100rpx;
			display: flex;
			justify-content: space-between;
			align-items: center;
			margin-bottom: 32rpx;

			.close-cont {
				color: #aaa;
				letter-spacing: 6rpx;
			}

			.text-info {
				display: flex;
				flex-direction: column;
				align-items: center;
			}

			.save-cont {
				background: #409EFF;
				color: #fff;
				width: 100rpx;
				height: 60rpx;
				text-align: center;
				line-height: 60rpx;
				border-radius: 6rpx;
			}
		}

		.tabs-cont {
			box-sizing: border-box;
			height: 80rpx;
			background-color: #F3F5F9;
			display: flex;
			align-items: center;
			border-radius: 4rpx;
			padding: 0 10rpx;

			.tab-list {
				min-width: 20%;
				display: flex;
				height: 60%;
				flex: 1;
				justify-content: center;
				border-radius: 4rpx;

				&.active {
					background-color: #fff;
				}
			}
		}

		.date-cont {
			height: calc(750rpx - 180rpx);

			.picker-view {
				width: 100%;
				height: 100%;

				.item {
					display: flex;
					justify-content: center;
					align-items: center;
				}

				.uni-picker-view-indicator {
					height: 100rpx;
				}
			}
		}
	}
</style>

utils文件

@/utils/getMonth.js
//获取这个月的月初和月末
export function getMonthStartEnd(vars){
  var str = '';
  if(vars!=null&&vars!=''){
    var nyYear=vars.slice(0,4);
    var nyMonth=vars.slice(5,vars.length);
    var firstDay = new Date(nyYear,nyMonth-1);
    var lastDay = new Date(new Date(nyYear,nyMonth).valueOf()-60*60*1000*24);
    function datasFormat(d){
      var datetime=d.getFullYear() + '-' + (d.getMonth() + 1) + '-' + d.getDate();
      return datetime;
    }
    str = datasFormat(firstDay) + "/" + datasFormat(lastDay)
  }
  return str
}
@/utils/getQuarter.js
export function getQuarterDates(year, quarter) {
	const startDate = new Date(year, (quarter - 1) * 3, 1);
	const endDate = new Date(year, quarter * 3, 0);

	const startDay = startDate.getDate();
	const endDay = endDate.getDate();

	const startMonth = startDate.getMonth() + 1;
	const endMonth = endDate.getMonth() + 1;

	const startYear = startDate.getFullYear();
	const endYear = endDate.getFullYear();

	const startDateString =
		`${startYear}-${startMonth < 10 ? '0' + startMonth : startMonth}-${startDay < 10 ? '0' + startDay : startDay}`;
	const endDateString =
		`${endYear}-${endMonth < 10 ? '0' + endMonth : endMonth}-${endDay < 10 ? '0' + endDay : endDay}`;

	return {
		startDate: startDateString,
		endDate: endDateString
	};
}

// // 示例用法:计算2024年第2季度的起止日期
// const quarterDates = getQuarterDates(2024,2);
// console.log(quarterDates.startDate); // 输出:2024-04-01
// console.log(quarterDates.endDate);   // 输出:2024-06-30
@/utils/getWeek.js
//时间戳转年月日  参数是秒的时间戳 函数返回一个对象 对象里有年 月 日
export function yearDay(long){
  var time = new Date(long * 1000)
  var year = time.getFullYear();
  var month = (time.getMonth()+1) < 10 ? '0' + (time.getMonth()+1) : (time.getMonth()+1);
  var date = time.getDate() < 10 ? '0' + time.getDate() : time.getDate() ;
  var yearday = {year,month,date}
  return yearday
}
//计算一年中的每一周都是从几号到几号
//第一周为1月1日到 本年的 第一个周日
//第二周为 本年的 第一个周一 往后推到周日
//以此类推 再往后推52周。。。
//如果最后一周在12月31日之前,则本年有垮了54周,反之53周
//12月31 日不论是周几,都算为本周的最后一天
//参数年份 ,函数返回一个数组,数组里的对象包含 这一周的开始日期和结束日期
export function whichWeek(year){
  var d = new Date(year, 0, 1);
  while (d.getDay() != 1) {
    d.setDate(d.getDate() + 1);
  }
  let arr = []
  let longnum = d.setDate(d.getDate())
  if(longnum > +new Date(year, 0, 1)){
    let obj = yearDay(+new Date(year, 0, 1) / 1000)
    obj.last = yearDay( longnum / 1000 - 86400)
    arr.push(obj)
  }
  let oneitem = yearDay(longnum / 1000)
  oneitem.last = yearDay( longnum / 1000 + 86400 * 6)
  arr.push(oneitem)
  var lastStr
  for(var i = 0 ; i<51 ;i++){
    let long = d.setDate(d.getDate() + 7)
    let obj = yearDay( long / 1000)
    obj.last = yearDay( long / 1000 + 86400 * 6)
    lastStr = long + 86400000 * 6
    arr.push(obj)
  }
  if(lastStr < +new Date(year + 1, 0, 1)){        
    let obj = yearDay(lastStr / 1000 + 86400)
    obj.last = yearDay(+new Date(year + 1, 0, 1) / 1000 - 86400)
    arr.push(obj)
  }else{
    arr[arr.length-1].last = yearDay(+new Date(year + 1, 0, 1) / 1000 - 86400)
  }
  return arr
}
// //例如 2012 年就跨了54周,也有很多是53周的
// let week2012 = whichWeek(2012)
// console.log(week2012)
// //调用完得到的是一个数组见下图

uniapp使用插件使用

https://ext.dcloud.net.cn/plugin?id=16387icon-default.png?t=N7T8https://ext.dcloud.net.cn/plugin?id=16387

zdp-date-picker使用说明

<zdp-date-picker ref="zdpdate" type="月" time="2023-2" @submit-date="handleDate($event)"></zdp-date-picker>

  • 属性参数说明:

ref用于控制picker的弹出与隐藏,组件内有open和close函数

this.$refs.zdpdate.open()
参数名作用类型默认值
type控制打开piker时tabs默认显示的位置String
time显示的默认日期String2023-1

type用于控制打开piker时显示的tabs;默认参数:日、周、月、季、年

time用于控制显示的默认日期;

type=“日” time="2024-1-23" 代表默认显示日、time代表选中日期2024年1月23日

type=“周” time=“2024-4” 代表默认显示周、time代表选中日期2024年第4周

type=“月” time=“2024-1” 代表默认显示月、time代表选中日期2024年1月

type=“季” time=“2024-1” 代表默认显示季、time代表选中日期2024年第1季度

type=“年” time=“2024” 代表默认显示年、time代表选中日期2024

  • 事件返回参数说明:
事件名作用
submit-date用于接收组件返回的时间

submit-date 用于接收组件返回的时间

range 是选中日期的时间范围

zdpdate是具体的固定日期

type 是选中日期类型 日、周、月、季、年

handleDate(e){
    let {range,zdpdate,type}=e
    this.range=range
    this.zdpdate=zdpdate
    this.type=type
}
  • 自定义插槽:

自定义显示组件内文本内容:

<zdp-date-picker ref="zdpdate" :type="type" time="2024-3"  
    @submit-date="handleDate($event)">
	<template #text="{scope}">
		{{scope.range}}
		{{scope.zdpdate}}
	</template>
</zdp-date-picker>