From d1448cb0ef10f358bb7bddb4e1ec268515e0b787 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期二, 15 七月 2025 11:46:57 +0800
Subject: [PATCH] 项目初始化

---
 uni_modules/uview-ui/components/u-calendar/u-calendar.vue |  383 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 383 insertions(+), 0 deletions(-)

diff --git a/uni_modules/uview-ui/components/u-calendar/u-calendar.vue b/uni_modules/uview-ui/components/u-calendar/u-calendar.vue
new file mode 100644
index 0000000..ad892ff
--- /dev/null
+++ b/uni_modules/uview-ui/components/u-calendar/u-calendar.vue
@@ -0,0 +1,383 @@
+<template>
+	<u-popup
+		:show="show"
+		mode="bottom"
+		closeable
+		@close="close"
+		:round="round"
+		:closeOnClickOverlay="closeOnClickOverlay"
+	>
+		<view class="u-calendar">
+			<uHeader
+				:title="title"
+				:subtitle="subtitle"
+				:showSubtitle="showSubtitle"
+				:showTitle="showTitle"
+			></uHeader>
+			<scroll-view
+				:style="{
+                    height: $u.addUnit(listHeight)
+                }"
+				scroll-y
+				@scroll="onScroll"
+				:scroll-top="scrollTop"
+				:scrollIntoView="scrollIntoView"
+			>
+				<uMonth
+					:color="color"
+					:rowHeight="rowHeight"
+					:showMark="showMark"
+					:months="months"
+					:mode="mode"
+					:maxCount="maxCount"
+					:startText="startText"
+					:endText="endText"
+					:defaultDate="defaultDate"
+					:minDate="innerMinDate"
+					:maxDate="innerMaxDate"
+					:maxMonth="monthNum"
+					:readonly="readonly"
+					:maxRange="maxRange"
+					:rangePrompt="rangePrompt"
+					:showRangePrompt="showRangePrompt"
+					:allowSameDay="allowSameDay"
+					ref="month"
+					@monthSelected="monthSelected"
+					@updateMonthTop="updateMonthTop"
+				></uMonth>
+			</scroll-view>
+			<slot name="footer" v-if="showConfirm">
+				<view class="u-calendar__confirm">
+					<u-button
+						shape="circle"
+						:text="
+                            buttonDisabled ? confirmDisabledText : confirmText
+                        "
+						:color="color"
+						@click="confirm"
+						:disabled="buttonDisabled"
+					></u-button>
+				</view>
+			</slot>
+		</view>
+	</u-popup>
+</template>
+
+<script>
+import uHeader from './header.vue'
+import uMonth from './month.vue'
+import props from './props.js'
+import util from './util.js'
+import dayjs from '../../libs/util/dayjs.js'
+import Calendar from '../../libs/util/calendar.js'
+/**
+ * Calendar 鏃ュ巻
+ * @description  姝ょ粍浠剁敤浜庡崟涓�夋嫨鏃ユ湡锛岃寖鍥撮�夋嫨鏃ユ湡绛夛紝鏃ュ巻琚寘瑁瑰湪搴曢儴寮硅捣鐨勫鍣ㄤ腑.
+ * @tutorial https://www.uviewui.com/components/calendar.html
+ *
+ * @property {String}				title				鏍囬鍐呭 (榛樿 鏃ユ湡閫夋嫨 )
+ * @property {Boolean}				showTitle			鏄惁鏄剧ず鏍囬  (榛樿 true )
+ * @property {Boolean}				showSubtitle		鏄惁鏄剧ず鍓爣棰�	(榛樿 true )
+ * @property {String}				mode				鏃ユ湡绫诲瀷閫夋嫨  single-閫夋嫨鍗曚釜鏃ユ湡锛宮ultiple-鍙互閫夋嫨澶氫釜鏃ユ湡锛宺ange-閫夋嫨鏃ユ湡鑼冨洿 锛� 榛樿 'single' )
+ * @property {String}				startText			mode=range鏃讹紝绗竴涓棩鏈熷簳閮ㄧ殑鎻愮ず鏂囧瓧  (榛樿 '寮�濮�' )
+ * @property {String}				endText				mode=range鏃讹紝鏈�鍚庝竴涓棩鏈熷簳閮ㄧ殑鎻愮ず鏂囧瓧 (榛樿 '缁撴潫' )
+ * @property {Array}				customList			鑷畾涔夊垪琛�
+ * @property {String}				color				涓婚鑹诧紝瀵瑰簳閮ㄦ寜閽拰閫変腑鏃ユ湡鏈夋晥  (榛樿 鈥�#3c9cff' )
+ * @property {String | Number}		minDate				鏈�灏忕殑鍙�夋棩鏈�	 (榛樿 0 )
+ * @property {String | Number}		maxDate				鏈�澶у彲閫夋棩鏈�  (榛樿 0 )
+ * @property {Array | String| Date}	defaultDate			榛樿閫変腑鐨勬棩鏈燂紝mode涓簃ultiple鎴杛ange鏄繀椤讳负鏁扮粍鏍煎紡
+ * @property {String | Number}		maxCount			mode=multiple鏃讹紝鏈�澶氬彲閫夊灏戜釜鏃ユ湡  (榛樿 	Number.MAX_SAFE_INTEGER  )
+ * @property {String | Number}		rowHeight			鏃ユ湡琛岄珮 (榛樿 56 )
+ * @property {Function}				formatter			鏃ユ湡鏍煎紡鍖栧嚱鏁�
+ * @property {Boolean}				showLunar			鏄惁鏄剧ず鍐滃巻  (榛樿 false )
+ * @property {Boolean}				showMark			鏄惁鏄剧ず鏈堜唤鑳屾櫙鑹� (榛樿 true )
+ * @property {String}				confirmText			纭畾鎸夐挳鐨勬枃瀛� (榛樿 '纭畾' )
+ * @property {String}				confirmDisabledText	纭鎸夐挳澶勪簬绂佺敤鐘舵�佹椂鐨勬枃瀛� (榛樿 '纭畾' )
+ * @property {Boolean}				show				鏄惁鏄剧ず鏃ュ巻寮圭獥 (榛樿 false )
+ * @property {Boolean}				closeOnClickOverlay	鏄惁鍏佽鐐瑰嚮閬僵鍏抽棴鏃ュ巻 (榛樿 false )
+ * @property {Boolean}				readonly	        鏄惁涓哄彧璇荤姸鎬侊紝鍙鐘舵�佷笅绂佹閫夋嫨鏃ユ湡 (榛樿 false )
+ * @property {String | Number}		maxRange	        鏃ユ湡鍖洪棿鏈�澶氬彲閫夊ぉ鏁帮紝榛樿鏃犻檺鍒讹紝mode = range鏃舵湁鏁�
+ * @property {String}				rangePrompt	        鑼冨洿閫夋嫨瓒呰繃鏈�澶氬彲閫夊ぉ鏁版椂鐨勬彁绀烘枃妗堬紝mode = range鏃舵湁鏁�
+ * @property {Boolean}				showRangePrompt	    鑼冨洿閫夋嫨瓒呰繃鏈�澶氬彲閫夊ぉ鏁版椂锛屾槸鍚﹀睍绀烘彁绀烘枃妗堬紝mode = range鏃舵湁鏁� (榛樿 true )
+ * @property {Boolean}				allowSameDay	    鏄惁鍏佽鏃ユ湡鑼冨洿鐨勮捣姝㈡椂闂翠负鍚屼竴澶╋紝mode = range鏃舵湁鏁� (榛樿 false )
+ * @property {Number|String}	    round				鍦嗚鍊硷紝榛樿鏃犲渾瑙�  (榛樿 0 )
+ * @property {Number|String}	    monthNum			鏈�澶氬睍绀虹殑鏈堜唤鏁伴噺  (榛樿 3 )
+ *
+ * @event {Function()} confirm 		鐐瑰嚮纭畾鎸夐挳鏃惰Е鍙�		閫夋嫨鏃ユ湡鐩稿叧鐨勮繑鍥炲弬鏁�
+ * @event {Function()} close 		鏃ュ巻鍏抽棴鏃惰Е鍙�			鍙畾涔夐〉闈㈠叧闂椂鐨勫洖璋冧簨浠�
+ * @example <u-calendar  :defaultDate="defaultDateMultiple" :show="show" mode="multiple" @confirm="confirm">
+	</u-calendar>
+ * */
+export default {
+	name: 'u-calendar',
+	mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
+	components: {
+		uHeader,
+		uMonth
+	},
+	data() {
+		return {
+			// 闇�瑕佹樉绀虹殑鏈堜唤鐨勬暟缁�
+			months: [],
+			// 鍦ㄦ湀浠芥粴鍔ㄥ尯鍩熶腑锛屽綋鍓嶈鍥句腑鏈堜唤鐨刬ndex绱㈠紩
+			monthIndex: 0,
+			// 鏈堜唤婊氬姩鍖哄煙鐨勯珮搴�
+			listHeight: 0,
+			// month缁勪欢涓�夋嫨鐨勬棩鏈熸暟缁�
+			selected: [],
+			scrollIntoView: '',
+			scrollTop:0,
+			// 杩囨护澶勭悊鏂规硶
+			innerFormatter: (value) => value
+		}
+	},
+	watch: {
+		selectedChange: {
+			immediate: true,
+			handler(n) {
+				this.setMonth()
+			}
+		},
+		// 鎵撳紑寮圭獥鏃讹紝璁剧疆鏈堜唤鏁版嵁
+		show: {
+			immediate: true,
+			handler(n) {
+				this.setMonth()
+			}
+		}
+	},
+	computed: {
+		// 鐢变簬maxDate鍜宮inDate鍙互涓哄瓧绗︿覆(2021-10-10)锛屾垨鑰呮暟鍊�(鏃堕棿鎴�)锛屼絾鏄痙ayjs濡傛灉鎺ュ彈瀛楃涓插舰寮忕殑鏃堕棿鎴充細鏈夐棶棰橈紝杩欓噷杩涜澶勭悊
+		innerMaxDate() {
+			return uni.$u.test.number(this.maxDate)
+				? Number(this.maxDate)
+				: this.maxDate
+		},
+		innerMinDate() {
+			return uni.$u.test.number(this.minDate)
+				? Number(this.minDate)
+				: this.minDate
+		},
+		// 澶氫釜鏉′欢鐨勫彉鍖栵紝浼氬紩璧烽�変腑鏃ユ湡鐨勫彉鍖栵紝杩欓噷缁熶竴绠$悊鐩戝惉
+		selectedChange() {
+			return [this.innerMinDate, this.innerMaxDate, this.defaultDate]
+		},
+		subtitle() {
+			// 鍒濆鍖栨椂锛宼his.months涓虹┖鏁扮粍锛屾墍浠ラ渶瑕佺壒鍒垽鏂鐞�
+			if (this.months.length) {
+				return `${this.months[this.monthIndex].year}骞�${
+					this.months[this.monthIndex].month
+				}鏈坄
+			} else {
+				return ''
+			}
+		},
+		buttonDisabled() {
+			// 濡傛灉涓簉ange绫诲瀷锛屼笖閫夋嫨鐨勬棩鏈熶釜鏁颁笉瓒�1涓椂锛岃搴曢儴鐨勬寜閽嚭浜巇isabled鐘舵��
+			if (this.mode === 'range') {
+				if (this.selected.length <= 1) {
+					return true
+				} else {
+					return false
+				}
+			} else {
+				return false
+			}
+		}
+	},
+	mounted() {
+		this.start = Date.now()
+		this.init()
+	},
+	methods: {
+		// 鍦ㄥ井淇″皬绋嬪簭涓紝涓嶆敮鎸佸皢鍑芥暟褰撳仛props鍙傛暟锛屾晠鍙兘閫氳繃ref褰㈠紡璋冪敤
+		setFormatter(e) {
+			this.innerFormatter = e
+		},
+		// month缁勪欢鍐呴儴閫夋嫨鏃ユ湡鍚庯紝閫氳繃浜嬩欢閫氱煡缁欑埗缁勪欢
+		monthSelected(e) {
+			this.selected = e
+			if (!this.showConfirm) {
+				// 鍦ㄤ笉闇�瑕佺‘璁ゆ寜閽殑鎯呭喌涓嬶紝濡傛灉涓哄崟閫夛紝鎴栬�呰寖鍥村閫変笖宸查�夐暱搴﹀ぇ浜�2锛屽垯鐩存帴杩涜杩旇繕
+				if (
+					this.mode === 'multiple' ||
+					this.mode === 'single' ||
+					(this.mode === 'range' && this.selected.length >= 2)
+				) {
+					this.$emit('confirm', this.selected)
+				}
+			}
+		},
+		init() {
+			// 鏍¢獙maxDate锛屼笉鑳藉皬浜庡綋鍓嶆椂闂�
+			if (
+				this.innerMaxDate &&
+				new Date(this.innerMaxDate).getTime() <= Date.now()
+			) {
+				return uni.$u.error('maxDate涓嶈兘灏忎簬褰撳墠鏃堕棿')
+			}
+			// 婊氬姩鍖哄煙鐨勯珮搴�
+			this.listHeight = this.rowHeight * 5 + 30
+			this.setMonth()
+		},
+		close() {
+			this.$emit('close')
+		},
+		// 鐐瑰嚮纭畾鎸夐挳
+		confirm() {
+			if (!this.buttonDisabled) {
+				this.$emit('confirm', this.selected)
+			}
+		},
+		// 鑾峰緱涓や釜鏃ユ湡涔嬮棿鐨勬湀浠芥暟
+		getMonths(minDate, maxDate) {
+			const minYear = dayjs(minDate).year()
+			const minMonth = dayjs(minDate).month() + 1
+			const maxYear = dayjs(maxDate).year()
+			const maxMonth = dayjs(maxDate).month() + 1
+			return (maxYear - minYear) * 12 + (maxMonth - minMonth) + 1
+		},
+		// 璁剧疆鏈堜唤鏁版嵁
+		setMonth() {
+			// 鏈�灏忔棩鏈熺殑姣鏁�
+			const minDate = this.innerMinDate || dayjs().valueOf()
+			// 濡傛灉娌℃湁鎸囧畾鏈�澶ф棩鏈燂紝鍒欏線鍚庢帹3涓湀
+			const maxDate =
+				this.innerMaxDate ||
+				dayjs(minDate)
+					.add(this.monthNum - 1, 'month')
+					.valueOf()
+			// 鏈�澶ф渶灏忔湀浠戒箣闂寸殑鍏辨湁澶氬皯涓湀浠斤紝
+			const months = uni.$u.range(
+				1,
+				this.monthNum,
+				this.getMonths(minDate, maxDate)
+			)
+			// 鍏堟竻绌烘暟缁�
+			this.months = []
+			for (let i = 0; i < months; i++) {
+				this.months.push({
+					date: new Array(
+						dayjs(minDate).add(i, 'month').daysInMonth()
+					)
+						.fill(1)
+						.map((item, index) => {
+							// 鏃ユ湡锛屽彇鍊�1-31
+							let day = index + 1
+							// 鏄熸湡锛�0-6锛�0涓哄懆鏃�
+							const week = dayjs(minDate)
+								.add(i, 'month')
+								.date(day)
+								.day()
+							const date = dayjs(minDate)
+								.add(i, 'month')
+								.date(day)
+								.format('YYYY-MM-DD')
+							let bottomInfo = ''
+							if (this.showLunar) {
+								// 灏嗘棩鏈熻浆涓哄啘鍘嗘牸寮�
+								const lunar = Calendar.solar2lunar(
+									dayjs(date).year(),
+									dayjs(date).month() + 1,
+									dayjs(date).date()
+								)
+								bottomInfo = lunar.IDayCn
+							}
+							let config = {
+								day,
+								week,
+								// 灏忎簬鏈�灏忓厑璁哥殑鏃ユ湡锛屾垨鑰呭ぇ浜庢渶澶х殑鏃ユ湡锛屽垯璁剧疆涓篸isabled鐘舵��
+								disabled:
+									dayjs(date).isBefore(
+										dayjs(minDate).format('YYYY-MM-DD')
+									) ||
+									dayjs(date).isAfter(
+										dayjs(maxDate).format('YYYY-MM-DD')
+									),
+								// 杩斿洖涓�涓棩鏈熷璞★紝渚涘閮ㄧ殑formatter鑾峰彇褰撳墠鏃ユ湡鐨勫勾鏈堟棩绛変俊鎭紝杩涜鍔犲伐澶勭悊
+								date: new Date(date),
+								bottomInfo,
+								dot: false,
+								month:
+									dayjs(minDate).add(i, 'month').month() + 1
+							}
+							const formatter =
+								this.formatter || this.innerFormatter
+							return formatter(config)
+						}),
+					// 褰撳墠鎵�灞炵殑鏈堜唤
+					month: dayjs(minDate).add(i, 'month').month() + 1,
+					// 褰撳墠骞翠唤
+					year: dayjs(minDate).add(i, 'month').year()
+				})
+			}
+
+		},
+		// 婊氬姩鍒伴粯璁よ缃殑鏈堜唤
+		scrollIntoDefaultMonth(selected) {
+			// 鏌ヨ榛樿鏃ユ湡鍦ㄥ彲閫夊垪琛ㄧ殑涓嬫爣
+			const _index = this.months.findIndex(({
+				  year,
+				  month
+			  }) => {
+				month = uni.$u.padZero(month)
+				return `${year}-${month}` === selected
+			})
+			if (_index !== -1) {
+				// #ifndef MP-WEIXIN
+				this.$nextTick(() => {
+					this.scrollIntoView = `month-${_index}`
+				})
+				// #endif
+				// #ifdef MP-WEIXIN
+				this.scrollTop = this.months[_index].top || 0;
+				// #endif
+			}
+		},
+		// scroll-view婊氬姩鐩戝惉
+		onScroll(event) {
+			// 涓嶅厑璁稿皬浜�0鐨勬粴鍔ㄥ�硷紝濡傛灉scroll-view鍒伴《浜嗭紝缁х画涓嬫媺锛屼細鍑虹幇璐熸暟鍊�
+			const scrollTop = Math.max(0, event.detail.scrollTop)
+			// 灏嗗綋鍓嶆粴鍔ㄦ潯鏁板�硷紝闄や互婊氬姩鍖哄煙鐨勯珮搴︼紝鍙互寰楀嚭褰撳墠婊氬姩鍒颁簡鍝竴涓湀浠界殑绱㈠紩
+			for (let i = 0; i < this.months.length; i++) {
+				if (scrollTop >= (this.months[i].top || this.listHeight)) {
+					this.monthIndex = i
+				}
+			}
+		},
+		// 鏇存柊鏈堜唤鐨則op鍊�
+		updateMonthTop(topArr = []) {
+			// 璁剧疆瀵瑰簲鏈堜唤鐨則op鍊硷紝鐢ㄤ簬onScroll鏂规硶鏇存柊鏈堜唤
+			topArr.map((item, index) => {
+				this.months[index].top = item
+			})
+
+			// 鑾峰彇榛樿鏃ユ湡鐨勪笅鏍�
+			if (!this.defaultDate) {
+				// 濡傛灉娌℃湁璁剧疆榛樿鏃ユ湡锛屽垯灏嗗綋澶╂棩鏈熻缃负榛樿閫変腑鐨勬棩鏈�
+				const selected = dayjs().format("YYYY-MM")
+				this.scrollIntoDefaultMonth(selected)
+				return
+			}
+			let selected = dayjs().format("YYYY-MM");
+			// 鍗曢�夋ā寮忥紝鍙互鏄瓧绗︿覆鎴栨暟缁勶紝Date瀵硅薄绛�
+			if (!uni.$u.test.array(this.defaultDate)) {
+				selected = dayjs(this.defaultDate).format("YYYY-MM")
+			} else {
+				selected = dayjs(this.defaultDate[0]).format("YYYY-MM");
+			}
+			this.scrollIntoDefaultMonth(selected)
+		}
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+@import '../../libs/css/components.scss';
+
+.u-calendar {
+	&__confirm {
+		padding: 7px 18px;
+	}
+}
+</style>

--
Gitblit v1.9.3