From f26f29d84e0a68831a6af14dab3eec5500496d2e Mon Sep 17 00:00:00 2001 From: spring <2396852758@qq.com> Date: 星期三, 28 五月 2025 16:48:52 +0800 Subject: [PATCH] 初始化项目 --- uview-ui/components/u-calendar/u-calendar.vue | 639 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 639 insertions(+), 0 deletions(-) diff --git a/uview-ui/components/u-calendar/u-calendar.vue b/uview-ui/components/u-calendar/u-calendar.vue new file mode 100644 index 0000000..bdef3d1 --- /dev/null +++ b/uview-ui/components/u-calendar/u-calendar.vue @@ -0,0 +1,639 @@ +<template> + <u-popup closeable :maskCloseAble="maskCloseAble" mode="bottom" :popup="false" v-model="value" length="auto" + :safeAreaInsetBottom="safeAreaInsetBottom" @close="close" :z-index="uZIndex" :border-radius="borderRadius" :closeable="closeable"> + <view class="u-calendar"> + <view class="u-calendar__header"> + <view class="u-calendar__header__text" v-if="!$slots['tooltip']"> + {{toolTip}} + </view> + <slot v-else name="tooltip" /> + </view> + <view class="u-calendar__action u-flex u-row-center"> + <view class="u-calendar__action__icon"> + <u-icon v-if="changeYear" name="arrow-left-double" :color="yearArrowColor" @click="changeYearHandler(0)"></u-icon> + </view> + <view class="u-calendar__action__icon"> + <u-icon v-if="changeMonth" name="arrow-left" :color="monthArrowColor" @click="changeMonthHandler(0)"></u-icon> + </view> + <view class="u-calendar__action__text">{{ showTitle }}</view> + <view class="u-calendar__action__icon"> + <u-icon v-if="changeMonth" name="arrow-right" :color="monthArrowColor" @click="changeMonthHandler(1)"></u-icon> + </view> + <view class="u-calendar__action__icon"> + <u-icon v-if="changeYear" name="arrow-right-double" :color="yearArrowColor" @click="changeYearHandler(1)"></u-icon> + </view> + </view> + <view class="u-calendar__week-day"> + <view class="u-calendar__week-day__text" v-for="(item, index) in weekDayZh" :key="index">{{item}}</view> + </view> + <view class="u-calendar__content"> + <!-- 鍓嶇疆绌虹櫧閮ㄥ垎 --> + <block v-for="(item, index) in weekdayArr" :key="index"> + <view class="u-calendar__content__item"></view> + </block> + <view class="u-calendar__content__item" :class="{ + 'u-hover-class':openDisAbled(year,month,index+1), + 'u-calendar__content--start-date': (mode == 'range' && startDate==`${year}-${month}-${index+1}`) || mode== 'date', + 'u-calendar__content--end-date':(mode== 'range' && endDate==`${year}-${month}-${index+1}`) || mode == 'date' + }" :style="{backgroundColor: getColor(index,1)}" v-for="(item, index) in daysArr" :key="index" + @tap="dateClick(index)"> + <view class="u-calendar__content__item__inner" :style="{color: getColor(index,2)}"> + <view>{{ index + 1 }}</view> + </view> + <view class="u-calendar__content__item__tips" :style="{color:activeColor}" v-if="mode== 'range' && startDate==`${year}-${month}-${index+1}` && startDate!=endDate">{{startText}}</view> + <view class="u-calendar__content__item__tips" :style="{color:activeColor}" v-if="mode== 'range' && endDate==`${year}-${month}-${index+1}`">{{endText}}</view> + </view> + <view class="u-calendar__content__bg-month">{{month}}</view> + </view> + <view class="u-calendar__bottom"> + <view class="u-calendar__bottom__choose"> + <text>{{mode == 'date' ? activeDate : startDate}}</text> + <text v-if="endDate">鑷硔{endDate}}</text> + </view> + <view class="u-calendar__bottom__btn"> + <u-button :type="btnType" shape="circle" size="default" @click="btnFix(false)">纭畾</u-button> + </view> + </view> + </view> + </u-popup> +</template> +<script> + /** + * calendar 鏃ュ巻 + * @description 姝ょ粍浠剁敤浜庡崟涓�夋嫨鏃ユ湡锛岃寖鍥撮�夋嫨鏃ユ湡绛夛紝鏃ュ巻琚寘瑁瑰湪搴曢儴寮硅捣鐨勫鍣ㄤ腑銆� + * @tutorial http://uviewui.com/components/calendar.html + * @property {String} mode 閫夋嫨鏃ユ湡鐨勬ā寮忥紝date-涓哄崟涓棩鏈燂紝range-涓洪�夋嫨鏃ユ湡鑼冨洿 + * @property {Boolean} v-model 甯冨皵鍊煎彉閲忥紝鐢ㄤ簬鎺у埗鏃ュ巻鐨勫脊鍑轰笌鏀惰捣 + * @property {Boolean} safe-area-inset-bottom 鏄惁寮�鍚簳閮ㄥ畨鍏ㄥ尯閫傞厤(榛樿false) + * @property {Boolean} change-year 鏄惁鏄剧ず椤堕儴鐨勫垏鎹㈠勾浠芥柟鍚戠殑鎸夐挳(榛樿true) + * @property {Boolean} change-month 鏄惁鏄剧ず椤堕儴鐨勫垏鎹㈡湀浠芥柟鍚戠殑鎸夐挳(榛樿true) + * @property {String Number} max-year 鍙垏鎹㈢殑鏈�澶у勾浠�(榛樿2050) + * @property {String Number} min-year 鍙垏鎹㈢殑鏈�灏忓勾浠�(榛樿1950) + * @property {String Number} min-date 鏈�灏忓彲閫夋棩鏈�(榛樿1950-01-01) + * @property {String Number} max-date 鏈�澶у彲閫夋棩鏈�(榛樿褰撳墠鏃ユ湡) + * @property {String Number} 寮圭獥椤堕儴宸﹀彸涓よ竟鐨勫渾瑙掑�硷紝鍗曚綅rpx(榛樿20) + * @property {Boolean} mask-close-able 鏄惁鍏佽閫氳繃鐐瑰嚮閬僵鍏抽棴鏃ュ巻(榛樿true) + * @property {String} month-arrow-color 鏈堜唤鍒囨崲鎸夐挳绠ご棰滆壊(榛樿#606266) + * @property {String} year-arrow-color 骞翠唤鍒囨崲鎸夐挳绠ご棰滆壊(榛樿#909399) + * @property {String} color 鏃ユ湡瀛椾綋鐨勯粯璁ら鑹�(榛樿#303133) + * @property {String} active-bg-color 璧峰/缁撴潫鏃ユ湡鎸夐挳鐨勮儗鏅壊(榛樿#497bff) + * @property {String Number} z-index 寮瑰嚭鏃剁殑z-index鍊�(榛樿10075) + * @property {String} active-color 璧峰/缁撴潫鏃ユ湡鎸夐挳鐨勫瓧浣撻鑹�(榛樿#ffffff) + * @property {String} range-bg-color 璧峰/缁撴潫鏃ユ湡涔嬮棿鐨勫尯鍩熺殑鑳屾櫙棰滆壊(榛樿rgba(41,121,255,0.13)) + * @property {String} range-color 閫夋嫨鑼冨洿鍐呭瓧浣撻鑹�(榛樿#497bff) + * @property {String} start-text 璧峰鏃ユ湡搴曢儴鐨勬彁绀烘枃瀛�(榛樿 '寮�濮�') + * @property {String} end-text 缁撴潫鏃ユ湡搴曢儴鐨勬彁绀烘枃瀛�(榛樿 '缁撴潫') + * @property {String} btn-type 搴曢儴纭畾鎸夐挳鐨勪富棰�(榛樿 'primary') + * @property {String} toolTip 椤堕儴鎻愮ず鏂囧瓧锛屽璁剧疆鍚嶄负tooltip鐨剆lot锛屾鍙傛暟灏嗗け鏁�(榛樿 '閫夋嫨鏃ユ湡') + * @property {Boolean} closeable 鏄惁鏄剧ず鍙充笂瑙掔殑鍏抽棴鍥炬爣(榛樿true) + * @example <u-calendar v-model="show" :mode="mode"></u-calendar> + */ + + export default { + name: 'u-calendar', + props: { + safeAreaInsetBottom: { + type: Boolean, + default: false + }, + // 鏄惁鍏佽閫氳繃鐐瑰嚮閬僵鍏抽棴Picker + maskCloseAble: { + type: Boolean, + default: true + }, + // 閫氳繃鍙屽悜缁戝畾鎺у埗缁勪欢鐨勫脊鍑轰笌鏀惰捣 + value: { + type: Boolean, + default: false + }, + // 寮瑰嚭鐨剒-index鍊� + zIndex: { + type: [String, Number], + default: 0 + }, + // 鏄惁鍏佽鍒囨崲骞翠唤 + changeYear: { + type: Boolean, + default: true + }, + // 鏄惁鍏佽鍒囨崲鏈堜唤 + changeMonth: { + type: Boolean, + default: true + }, + // date-鍗曚釜鏃ユ湡閫夋嫨锛宺ange-寮�濮嬫棩鏈�+缁撴潫鏃ユ湡閫夋嫨 + mode: { + type: String, + default: 'date' + }, + // 鍙垏鎹㈢殑鏈�澶у勾浠� + maxYear: { + type: [Number, String], + default: 2050 + }, + // 鍙垏鎹㈢殑鏈�灏忓勾浠� + minYear: { + type: [Number, String], + default: 1950 + }, + // 鏈�灏忓彲閫夋棩鏈�(涓嶅湪鑼冨洿鍐呮棩鏈熺鐢ㄤ笉鍙��) + minDate: { + type: [Number, String], + default: '1950-01-01' + }, + /** + * 鏈�澶у彲閫夋棩鏈� + * 榛樿鏈�澶у�间负浠婂ぉ锛屼箣鍚庣殑鏃ユ湡涓嶅彲閫� + * 2030-12-31 + * */ + maxDate: { + type: [Number, String], + default: '' + }, + // 寮圭獥椤堕儴宸﹀彸涓よ竟鐨勫渾瑙掑�� + borderRadius: { + type: [String, Number], + default: 20 + }, + // 鏈堜唤鍒囨崲鎸夐挳绠ご棰滆壊 + monthArrowColor: { + type: String, + default: '#606266' + }, + // 骞翠唤鍒囨崲鎸夐挳绠ご棰滆壊 + yearArrowColor: { + type: String, + default: '#909399' + }, + // 榛樿鏃ユ湡瀛椾綋棰滆壊 + color: { + type: String, + default: '#303133' + }, + // 閫変腑|璧峰缁撴潫鏃ユ湡鑳屾櫙鑹� + activeBgColor: { + type: String, + default: '#497bff' + }, + // 閫変腑|璧峰缁撴潫鏃ユ湡瀛椾綋棰滆壊 + activeColor: { + type: String, + default: '#ffffff' + }, + // 鑼冨洿鍐呮棩鏈熻儗鏅壊 + rangeBgColor: { + type: String, + default: 'rgba(41,121,255,0.13)' + }, + // 鑼冨洿鍐呮棩鏈熷瓧浣撻鑹� + rangeColor: { + type: String, + default: '#497bff' + }, + // mode=range鏃剁敓鏁堬紝璧峰鏃ユ湡鑷畾涔夋枃妗� + startText: { + type: String, + default: '寮�濮�' + }, + // mode=range鏃剁敓鏁堬紝缁撴潫鏃ユ湡鑷畾涔夋枃妗� + endText: { + type: String, + default: '缁撴潫' + }, + //鎸夐挳鏍峰紡绫诲瀷 + btnType: { + type: String, + default: 'primary' + }, + // 褰撳墠閫変腑鏃ユ湡甯﹂�変腑鏁堟灉 + isActiveCurrent: { + type: Boolean, + default: true + }, + // 鍒囨崲骞存湀鏄惁瑙﹀彂浜嬩欢 mode=date鏃剁敓鏁� + isChange: { + type: Boolean, + default: false + }, + // 鏄惁鏄剧ず鍙充笂瑙掔殑鍏抽棴鍥炬爣 + closeable: { + type: Boolean, + default: true + }, + // 椤堕儴鐨勬彁绀烘枃瀛� + toolTip: { + type: String, + default: '閫夋嫨鏃ユ湡' + } + }, + data() { + return { + // 鏄熸湡鍑�,鍊间负1-7 + weekday: 1, + weekdayArr:[], + // 褰撳墠鏈堟湁澶氬皯澶� + days: 0, + daysArr:[], + showTitle: '', + year: 2020, + month: 0, + day: 0, + startYear: 0, + startMonth: 0, + startDay: 0, + endYear: 0, + endMonth: 0, + endDay: 0, + today: '', + activeDate: '', + startDate: '', + endDate: '', + isStart: true, + min: null, + max: null, + weekDayZh: ['鏃�', '涓�', '浜�', '涓�', '鍥�', '浜�', '鍏�'] + }; + }, + computed: { + dataChange() { + return `${this.mode}-${this.minDate}-${this.maxDate}`; + }, + uZIndex() { + // 濡傛灉鐢ㄦ埛鏈変紶閫抸-index鍊硷紝浼樺厛浣跨敤 + return this.zIndex ? this.zIndex : this.$u.zIndex.popup; + } + }, + watch: { + dataChange(val) { + this.init() + } + }, + created() { + this.init() + }, + methods: { + getColor(index, type) { + let color = type == 1 ? '' : this.color; + let day = index + 1 + let date = `${this.year}-${this.month}-${day}` + let timestamp = new Date(date.replace(/\-/g, '/')).getTime(); + let start = this.startDate.replace(/\-/g, '/') + let end = this.endDate.replace(/\-/g, '/') + if ((this.isActiveCurrent && this.activeDate == date) || this.startDate == date || this.endDate == date) { + color = type == 1 ? this.activeBgColor : this.activeColor; + } else if (this.endDate && timestamp > new Date(start).getTime() && timestamp < new Date(end).getTime()) { + color = type == 1 ? this.rangeBgColor : this.rangeColor; + } + return color; + }, + init() { + let now = new Date(); + this.year = now.getFullYear(); + this.month = now.getMonth() + 1; + this.day = now.getDate(); + this.today = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`; + this.activeDate = this.today; + this.min = this.initDate(this.minDate); + this.max = this.initDate(this.maxDate || this.today); + this.startDate = ""; + this.startYear = 0; + this.startMonth = 0; + this.startDay = 0; + this.endYear = 0; + this.endMonth = 0; + this.endDay = 0; + this.endDate = ""; + this.isStart = true; + this.changeData(); + }, + //鏃ユ湡澶勭悊 + initDate(date) { + let fdate = date.split('-'); + return { + year: Number(fdate[0] || 1920), + month: Number(fdate[1] || 1), + day: Number(fdate[2] || 1) + } + }, + openDisAbled: function(year, month, day) { + let bool = true; + let date = `${year}/${month}/${day}`; + // let today = this.today.replace(/\-/g, '/'); + let min = `${this.min.year}/${this.min.month}/${this.min.day}`; + let max = `${this.max.year}/${this.max.month}/${this.max.day}`; + let timestamp = new Date(date).getTime(); + if (timestamp >= new Date(min).getTime() && timestamp <= new Date(max).getTime()) { + bool = false; + } + return bool; + }, + generateArray: function(start, end) { + return Array.from(new Array(end + 1).keys()).slice(start); + }, + formatNum: function(num) { + return num < 10 ? '0' + num : num + ''; + }, + //涓�涓湀鏈夊灏戝ぉ + getMonthDay(year, month) { + let days = new Date(year, month, 0).getDate(); + return days; + }, + getWeekday(year, month) { + let date = new Date(`${year}/${month}/01 00:00:00`); + return date.getDay(); + }, + checkRange(year) { + let overstep = false; + if (year < this.minYear || year > this.maxYear) { + uni.showToast({ + title: "鏃ユ湡瓒呭嚭鑼冨洿鍟", + icon: 'none' + }) + overstep = true; + } + return overstep; + }, + changeMonthHandler(isAdd) { + if (isAdd) { + let month = this.month + 1; + let year = month > 12 ? this.year + 1 : this.year; + if (!this.checkRange(year)) { + this.month = month > 12 ? 1 : month; + this.year = year; + this.changeData(); + } + + } else { + let month = this.month - 1; + let year = month < 1 ? this.year - 1 : this.year; + if (!this.checkRange(year)) { + this.month = month < 1 ? 12 : month; + this.year = year; + this.changeData(); + } + } + }, + changeYearHandler(isAdd) { + let year = isAdd ? this.year + 1 : this.year - 1; + if (!this.checkRange(year)) { + this.year = year; + this.changeData(); + } + }, + changeData() { + this.days = this.getMonthDay(this.year, this.month); + this.daysArr=this.generateArray(1,this.days) + this.weekday = this.getWeekday(this.year, this.month); + this.weekdayArr=this.generateArray(1,this.weekday) + this.showTitle = `${this.year}骞�${this.month}鏈坄; + if (this.isChange && this.mode == 'date') { + this.btnFix(true); + } + }, + dateClick: function(day) { + day += 1; + if (!this.openDisAbled(this.year, this.month, day)) { + this.day = day; + let date = `${this.year}-${this.month}-${day}`; + if (this.mode == 'date') { + this.activeDate = date; + } else { + let compare = new Date(date.replace(/\-/g, '/')).getTime() < new Date(this.startDate.replace(/\-/g, '/')).getTime() + if (this.isStart || compare) { + this.startDate = date; + this.startYear = this.year; + this.startMonth = this.month; + this.startDay = this.day; + this.endYear = 0; + this.endMonth = 0; + this.endDay = 0; + this.endDate = ""; + this.activeDate = ""; + this.isStart = false; + } else { + this.endDate = date; + this.endYear = this.year; + this.endMonth = this.month; + this.endDay = this.day; + this.isStart = true; + } + } + } + }, + close() { + // 淇敼閫氳繃v-model缁戝畾鐨勭埗缁勪欢鍙橀噺鐨勫�间负false锛屼粠鑰岄殣钘忔棩鍘嗗脊绐� + this.$emit('input', false); + }, + getWeekText(date) { + date = new Date(`${date.replace(/\-/g, '/')} 00:00:00`); + let week = date.getDay(); + return '鏄熸湡' + ['鏃�', '涓�', '浜�', '涓�', '鍥�', '浜�', '鍏�'][week]; + }, + btnFix(show) { + if (!show) { + this.close(); + } + if (this.mode == 'date') { + let arr = this.activeDate.split('-') + let year = this.isChange ? this.year : Number(arr[0]); + let month = this.isChange ? this.month : Number(arr[1]); + let day = this.isChange ? this.day : Number(arr[2]); + //褰撳墠鏈堟湁澶氬皯澶� + let days = this.getMonthDay(year, month); + let result = `${year}-${this.formatNum(month)}-${this.formatNum(day)}`; + let weekText = this.getWeekText(result); + let isToday = false; + if (`${year}-${month}-${day}` == this.today) { + //浠婂ぉ + isToday = true; + } + this.$emit('change', { + year: year, + month: month, + day: day, + days: days, + result: result, + week: weekText, + isToday: isToday, + // switch: show //鏄惁鏄垏鎹㈠勾鏈堟搷浣� + }); + } else { + if (!this.startDate || !this.endDate) return; + let startMonth = this.formatNum(this.startMonth); + let startDay = this.formatNum(this.startDay); + let startDate = `${this.startYear}-${startMonth}-${startDay}`; + let startWeek = this.getWeekText(startDate) + + let endMonth = this.formatNum(this.endMonth); + let endDay = this.formatNum(this.endDay); + let endDate = `${this.endYear}-${endMonth}-${endDay}`; + let endWeek = this.getWeekText(endDate); + this.$emit('change', { + startYear: this.startYear, + startMonth: this.startMonth, + startDay: this.startDay, + startDate: startDate, + startWeek: startWeek, + endYear: this.endYear, + endMonth: this.endMonth, + endDay: this.endDay, + endDate: endDate, + endWeek: endWeek + }); + } + } + } + }; +</script> + +<style scoped lang="scss"> + @import "../../libs/css/style.components.scss"; + + .u-calendar { + color: $u-content-color; + + &__header { + width: 100%; + box-sizing: border-box; + font-size: 30rpx; + background-color: #fff; + color: $u-main-color; + + &__text { + margin-top: 30rpx; + padding: 0 60rpx; + @include vue-flex; + justify-content: center; + align-items: center; + } + } + + &__action { + padding: 40rpx 0 40rpx 0; + + &__icon { + margin: 0 16rpx; + } + + &__text { + padding: 0 16rpx; + color: $u-main-color; + font-size: 32rpx; + line-height: 32rpx; + font-weight: bold; + } + } + + &__week-day { + @include vue-flex; + align-items: center; + justify-content: center; + padding: 6px 0; + overflow: hidden; + + &__text { + flex: 1; + text-align: center; + } + } + + &__content { + width: 100%; + @include vue-flex; + flex-wrap: wrap; + padding: 6px 0; + box-sizing: border-box; + background-color: #fff; + position: relative; + + &--end-date { + border-top-right-radius: 8rpx; + border-bottom-right-radius: 8rpx; + } + + &--start-date { + border-top-left-radius: 8rpx; + border-bottom-left-radius: 8rpx; + } + + &__item { + width: 14.2857%; + @include vue-flex; + align-items: center; + justify-content: center; + padding: 6px 0; + overflow: hidden; + position: relative; + z-index: 2; + + &__inner { + height: 84rpx; + @include vue-flex; + align-items: center; + justify-content: center; + flex-direction: column; + font-size: 32rpx; + position: relative; + border-radius: 50%; + + &__desc { + width: 100%; + font-size: 24rpx; + line-height: 24rpx; + transform: scale(0.75); + transform-origin: center center; + position: absolute; + left: 0; + text-align: center; + bottom: 2rpx; + } + } + + &__tips { + width: 100%; + font-size: 24rpx; + line-height: 24rpx; + position: absolute; + left: 0; + transform: scale(0.8); + transform-origin: center center; + text-align: center; + bottom: 8rpx; + z-index: 2; + } + } + + &__bg-month { + position: absolute; + font-size: 130px; + line-height: 130px; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + color: #e4e7ed; + z-index: 1; + } + } + + &__bottom { + width: 100%; + @include vue-flex; + align-items: center; + justify-content: center; + flex-direction: column; + background-color: #fff; + padding: 0 40rpx 30rpx; + box-sizing: border-box; + font-size: 24rpx; + color: $u-tips-color; + + &__choose { + height: 50rpx; + } + + &__btn { + width: 100%; + } + } + } +</style> \ No newline at end of file -- Gitblit v1.9.3