日历插件
- 效果图
- 一、使用方法
- 二、组件编写,两个文件、直接上代码
-
效果图

一、使用方法
<template><view><view class="" @click="open"><text>展示日历{{value[0]}}-{{value[1]}}</text></view><calendar v-if="show" @change="change" @close="close" :show.sync="show" v-model="value" :option="dateOption"></calendar></view>
</template><script>export default {data() {return {dateOption: {formatter:'yyyy/mm/dd',minDate:'2023/01/05', maxDate: '2023/11/11',dateNum: 2, minRange: 0, startText: '开始', endText: '结束',},value: [],show: false,}},methods: {open(){this.show = true;},change(){},close(){}}}
</script>
二、组件编写,两个文件、直接上代码
month.vue
<template><view class="month-box"><view class="box-title"><text class="titletext">{{year}}年{{month}}月</text></view><view class="month"><view class="item" v-for="item in days"><view @click="selectDay(item)":class="{'item-con': true, 'itemno': !item.isSelect, 'item-sel': (item.timeNo==value[0] || item.timeNo==value[1]), 'item-jian': (item.timeNo>value[0] && item.timeNo<value[1])}"v-if="item"><text class="text">{{item.date}}</text><text class="kai" v-if="item.timeNo==value[0]">{{startText}}</text><text class="jie" v-if="item.timeNo==value[1]">{{endText}}</text></view></view></view></view>
</template><script>export default {props: {year: {type: String | Number,default: '2023'},startText: {type: String,default: '开始'},endText: {type: String,default: '结束'},month: {type: String | Number,default: '4'},maxDate: {type: Boolean | Number,default: false},minDate: {type: Boolean | Number,default: false},value: {type: Array,default: () => {return []}}},data() {return {day: 24 * 3600 * 1000,max: 0,min: new Date(this.year + '/' + this.month).getTime(),}},methods: {selectDay(day) {if(day.isSelect){this.$emit('change', day.timeNo)}},},computed: {days() {let dataArr = [];if(new Date(this.min).getDay() == 0){dataArr[5] = undefined;} else if(new Date(this.min).getDay()>=2){dataArr[new Date(this.min).getDay()-2] = undefined;}if (this.month == 12) {this.max = new Date((this.year - 0 + 1) + '/1').getTime()} else {this.max = new Date(this.year + '/' + (this.month - 0 + 1)).getTime()}let i = 0;let isSelect = true;while (this.min + i * this.day < this.max) {if (this.maxDate && this.min + i * this.day > this.maxDate) {dataArr.push({date: i + 1,isSelect: false,timeNo: this.min + i * this.day})} else if (this.minDate && this.min + i * this.day < this.minDate) {dataArr.push({date: i + 1,isSelect: false,timeNo: this.min + i * this.day})} else {dataArr.push({date: i + 1,isSelect: true,timeNo: this.min + i * this.day})}i++}return dataArr;}}}
</script><style scoped lang="less">.box-title {display: flex;align-items: center;justify-content: center;.titletext {color: #000;padding-top: 30rpx;font-size: 32rpx;font-weight: bold;padding-bottom: 20rpx;}}.month {padding: 0 25rpx;display: flex;flex-flow: row;flex-wrap: wrap;.item {width: 100rpx;height: 100rpx;.item-con {width: 100rpx;height: 100rpx;color: #333;display: flex;flex-flow: column;justify-content: center;align-items: center;&.itemno {color: #999;}&.item-sel {background-color: rgb(60, 156, 255);.text {color: white;}.kai {font-size: 20rpx;line-height: 1;color: white;}.jie {font-size: 20rpx;line-height: 1;color: white;}}&.item-jian {background-color: rgb(236, 245, 255);.text {color: rgb(60, 156, 255);}}}}}
</style>
calendar.vue
<template><view class="calendar" v-if="show"><view class="bg" @click="close"></view><view class="con"><view class="con-top"><view class="con-title"><view class="con-clear" @click="clear"><text class="textclear">清除</text></view><view class="title"><text class="texttitle">日期选择</text></view><view class="con-cancle" @click="close"><text class="cancle">关闭</text></view></view><view class="date"><text v-for="i in zhou">{{i}}</text></view></view><view class="con-center"><scroll-view class="scroll" scroll-y="true" :scroll-into-view="toview" scroll-top="120"><month @change="change" :startText="option.startText" :endText="option.endText" :id="'a'+item.year+item.month" v-for="item in monthArr":maxDate="option.maxDate" :minDate="option.minDate" :value="selectValue" :month="item.month":year="item.year"></month></scroll-view></view><view class="con-button" @click="sureDate" :class="{on: isClick}"><text class="sure">确定</text></view></view></view>
</template><script>import month from "./month.vue"export default {props: {show: {type: Boolean,default: false},option: {type: Object,default: {}},value: {type: Array,default: []}},components: {month},computed: {isClick(){return (this.selectValue.length == this.option.dateNum) || this.selectValue.length == 2}},data() {let value = [];if (this.value) {if (this.value[0]) {value[0] = new Date(new Date(this.value[0]).toDateString()).getTime();}if (this.value[1]) {value[1] = new Date(new Date(this.value[1]).toDateString()).getTime()}}return {toview: '',zhou: ['一', '二', '三', '四', '五', '六', '日'],monthArr: [],selectValue: value,isShow: false}},mounted() {this.option.maxDate = new Date(this.option.maxDate).getTime() || new Date().getTime() + 183 * 24 * 3600 * 1000this.option.minDate = new Date(this.option.minDate).getTime() || new Date().getTime() - 183 * 24 * 3600 * 1000let maxYear = new Date(this.option.maxDate).getFullYear();let maxMonth = new Date(this.option.maxDate).getMonth() + 1;let minYear = new Date(this.option.minDate).getFullYear();let minMonth = new Date(this.option.minDate).getMonth()+1;let i = 0;let monthArr = [];while (new Date(new Date(minYear + '/' + minMonth).setMonth(minMonth - 1 + i)).getTime() <= new Date(maxYear +'/' + maxMonth).getTime()) {monthArr.push({year: new Date(new Date(minYear + '/' + minMonth).setMonth(minMonth - 1 + i)).getFullYear(),month: new Date(new Date(minYear + '/' + minMonth).setMonth(minMonth - 1 + i)).getMonth() + 1});i++;}this.monthArr = monthArr;let toview = '';if (this.selectValue[0]) {toview = 'a' + new Date(this.selectValue[0]).getFullYear() + (new Date(this.selectValue[0]).getMonth() + 1)}this.$nextTick(() => {this.toview = toview})},methods: {formatter(date, formatter){let year = new Date(date).getFullYear()let month = new Date(date).getMonth()+1let day = new Date(date).getDate()return formatter.replace(/yyyy/i, year).replace(/mm/i, month).replace(/dd/i, day)},sureDate(){if(!this.isClick) returnlet value = [];if(this.option.formatter){value = [this.formatter(this.selectValue[0], this.option.formatter)]if(this.selectValue[1]){value.push(this.formatter(this.selectValue[1], this.option.formatter))}}this.$emit('input', value)this.$emit('update:show', false)this.$emit('change')this.$emit('close')},close(){this.$emit('update:show', false)this.$emit('close')},clear(){this.$emit('input', [])this.$emit('update:show', false)this.$emit('change', [])this.$emit('close')},change(day) {if(this.option.dateNum && this.option.dateNum == 1){this.selectValue = [day];return;}if (!this.selectValue.length) {this.selectValue = [day];} else if (this.selectValue.length == 1) {if (this.selectValue[0] > day) {this.selectValue = [day]} else {if (this.option.minRange) {if (this.option.minRange * 3600 * 24 * 1000 + this.selectValue[0] > day) {uni.showToast({icon: 'none',title: `选择间隔时间必须大于${this.option.minRange}天`})} else {this.selectValue = [this.selectValue[0], day];}} else {this.selectValue = [this.selectValue[0], day];}}} else {this.selectValue = [day]}}}}
</script><style scoped lang="less">.calendar {position: fixed;bottom: 0;left: 0;right: 0;top: 0;height: 100vh;.bg {width: 750rpx;height: 100vh;background: rgba(0, 0, 0, 0.5);}.con {position: absolute;left: 0;bottom: 0;right: 0;background: #fff;padding-top: 20rpx;.con-title {display: flex;height: 80rpx;flex-flow: row;justify-content: space-between;align-items: center;padding: 0 40rpx;padding-bottom: 20rpx;}.date {display: flex;flex-flow: row;height: 60rpx;justify-content: space-between;align-items: center;padding: 0 40rpx;border-bottom: 2rpx solid #eee;}.con-center {height: 800rpx;padding: 20rpx 0;.scroll {height: 800rpx;.item {height: 500rpx;}}}.con-button{height: 80rpx;width: 650rpx;display: flex;align-items: center;justify-content: center;margin: 20rpx 50rpx;background-color: rgb(60, 156, 255);border-radius: 40rpx;opacity: .5;&.on{opacity: 1;}.sure{font-size: 40rpx;color: #fff;}}}}
</style>