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-index-list/u-index-list.vue |  440 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 440 insertions(+), 0 deletions(-)

diff --git a/uni_modules/uview-ui/components/u-index-list/u-index-list.vue b/uni_modules/uview-ui/components/u-index-list/u-index-list.vue
new file mode 100644
index 0000000..d712618
--- /dev/null
+++ b/uni_modules/uview-ui/components/u-index-list/u-index-list.vue
@@ -0,0 +1,440 @@
+<template>
+	<view class="u-index-list">
+		<!-- #ifdef APP-NVUE -->
+		<list
+			:scrollTop="scrollTop"
+			enable-back-to-top
+			:offset-accuracy="1"
+			:style="{
+				maxHeight: $u.addUnit(scrollViewHeight)
+			}"
+			@scroll="scrollHandler"
+			ref="uList"
+		>
+			<cell
+				v-if="$slots.header"
+				ref="header"
+			>
+				<slot name="header" />
+			</cell>
+			<slot />
+			<cell v-if="$slots.footer">
+				<slot name="footer" />
+			</cell>
+		</list>
+		<!-- #endif -->
+		<!-- #ifndef APP-NVUE -->
+		<scroll-view
+			:scrollTop="scrollTop"
+			:scrollIntoView="scrollIntoView"
+			:offset-accuracy="1"
+			:style="{
+				maxHeight: $u.addUnit(scrollViewHeight)
+			}"
+			scroll-y
+			@scroll="scrollHandler"
+			ref="uList"
+		>
+			<view v-if="$slots.header">
+				<slot name="header" />
+			</view>
+			<slot />
+			<view v-if="$slots.footer">
+				<slot name="footer" />
+			</view>
+		</scroll-view>
+		<!-- #endif -->
+		<view
+			class="u-index-list__letter"
+			ref="u-index-list__letter"
+			:style="{ top: $u.addUnit(letterInfo.top || 100) }"
+			@touchstart="touchStart"
+			@touchmove.stop.prevent="touchMove"
+			@touchend.stop.prevent="touchEnd"
+			@touchcancel.stop.prevent="touchEnd"
+		>
+			<view
+				class="u-index-list__letter__item"
+				v-for="(item, index) in uIndexList"
+				:key="index"
+				:style="{
+					backgroundColor: activeIndex === index ? activeColor : 'transparent'
+				}"
+			>
+				<text
+					class="u-index-list__letter__item__index"
+					:style="{color: activeIndex === index ? '#fff' : inactiveColor}"
+				>{{ item }}</text>
+			</view>
+		</view>
+		<u-transition
+			mode="fade"
+			:show="touching"
+			:customStyle="{
+				position: 'fixed',
+				right: '50px',
+				top: $u.addUnit(indicatorTop),
+				zIndex: 2
+			}"
+		>
+			<view
+				class="u-index-list__indicator"
+				:class="['u-index-list__indicator--show']"
+				:style="{
+					height: $u.addUnit(indicatorHeight),
+					width: $u.addUnit(indicatorHeight)
+				}"
+			>
+				<text class="u-index-list__indicator__text">{{ uIndexList[activeIndex] }}</text>
+			</view>
+		</u-transition>
+	</view>
+</template>
+
+<script>
+	const indexList = () => {
+		const indexList = [];
+		const charCodeOfA = 'A'.charCodeAt(0);
+		for (let i = 0; i < 26; i++) {
+			indexList.push(String.fromCharCode(charCodeOfA + i));
+		}
+		return indexList;
+	}
+	import props from './props.js';
+	// #ifdef APP-NVUE
+	// 鐢变簬weex涓洪樋閲岀殑KPI涓氱哗鑰冩牳鐨勪骇鐗╋紝鎵�浠ヤ笉鏀寔鐧惧垎姣斿崟浣嶏紝杩欓噷闇�瑕侀�氳繃dom鏌ヨ缁勪欢鐨勫搴�
+	const dom = uni.requireNativePlugin('dom')
+	// #endif
+	/**
+	 * IndexList 绱㈠紩鍒楄〃
+	 * @description  閫氳繃鎶樺彔闈㈡澘鏀剁撼鍐呭鍖哄煙
+	 * @tutorial https://uviewui.com/components/indexList.html
+	 * @property {String}			inactiveColor	鍙宠竟閿氱偣闈炴縺娲荤殑棰滆壊 ( 榛樿 '#606266' )
+	 * @property {String}			activeColor		鍙宠竟閿氱偣婵�娲荤殑棰滆壊 ( 榛樿 '#5677fc' )
+	 * @property {Array}			indexList		绱㈠紩瀛楃鍒楄〃锛屾暟缁勫舰寮�
+	 * @property {Boolean}			sticky			鏄惁寮�鍚敋鐐硅嚜鍔ㄥ惛椤� ( 榛樿 true )
+	 * @property {String | Number}	customNavHeight	鑷畾涔夊鑸爮鐨勯珮搴� ( 榛樿 0 )
+	 * */ 
+	export default {
+		name: 'u-index-list',
+		mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
+		// #ifdef MP-WEIXIN
+		// 灏嗚嚜瀹氫箟鑺傜偣璁剧疆鎴愯櫄鎷熺殑锛屾洿鍔犳帴杩慥ue缁勪欢鐨勮〃鐜帮紝鑳芥洿濂界殑浣跨敤flex灞炴��
+		options: {
+			virtualHost: true
+		},
+		// #endif
+		data() {
+			return {
+				// 褰撳墠姝e湪琚�変腑鐨勫瓧姣嶇储寮�
+				activeIndex: -1,
+				touchmoveIndex: 1,
+				// 绱㈠紩瀛楁瘝鐨勪俊鎭�
+				letterInfo: {
+					height: 0,
+					itemHeight: 0,
+					top: 0
+				},
+				// 璁剧疆瀛楁瘝鎸囩ず鍣ㄧ殑楂樺害锛屽悗闈负浜嗚鎸囩ず鍣ㄨ窡闅忓瓧姣嶏紝骞跺皢灏栬閮ㄥ垎鎸囧悜瀛楁瘝鐨勪腑閮紝闇�瑕佷緷璧栨鍊�
+				indicatorHeight: 50,
+				// 瀛楁瘝鏀惧ぇ鎸囩ず鍣ㄧ殑top鍊硷紝涓轰簡璁╁叾鎸囧悜褰撳墠婵�娲荤殑瀛楁瘝
+				// indicatorTop: 0
+				// 褰撳墠鏄惁姝e湪琚Е鎽哥姸鎬�
+				touching: false,
+				// 婊氬姩鏉¢《閮╰op鍊�
+				scrollTop: 0,
+				// scroll-view鐨勯珮搴�
+				scrollViewHeight: 0,
+				// 绯荤粺淇℃伅
+				sys: uni.$u.sys(),
+				scrolling: false,
+				scrollIntoView: '',
+			}
+		},
+		computed: {
+			// 濡傛灉鏈変紶鍏ュ閮ㄧ殑indexList閿氱偣鏁扮粍鍒欎娇鐢紝鍚﹀垯浣跨敤鍐呴儴鐢熸垚A-Z瀛楁瘝
+			uIndexList() {
+				return this.indexList.length ? this.indexList : indexList()
+			},
+			// 瀛楁瘝鏀惧ぇ鎸囩ず鍣ㄧ殑top鍊硷紝涓轰簡璁╁叾鎸囧悜褰撳墠婵�娲荤殑瀛楁瘝
+			indicatorTop() {
+				const {
+					top,
+					itemHeight
+				} = this.letterInfo
+				return Math.floor(top + itemHeight * this.activeIndex + itemHeight / 2 - this.indicatorHeight / 2)
+			}
+		},
+		watch: {
+			// 鐩戝惉瀛楁瘝绱㈠紩鐨勫彉鍖栵紝閲嶆柊璁剧疆灏哄
+			uIndexList: {
+				immediate: true,
+				handler() {
+					uni.$u.sleep().then(() => {
+						this.setIndexListLetterInfo()
+					})
+				}
+			}
+		},
+		created() {
+			this.children = []
+			this.anchors = []
+			this.init()
+		},
+		mounted() {
+			this.setIndexListLetterInfo()
+		},
+		methods: {
+			init() {
+				// 璁剧疆鍒楄〃鐨勯珮搴︿负鏁翠釜灞忓箷鐨勯珮搴�
+				//鍑忓幓this.customNavHeight锛屽苟灏唗his.scrollViewHeight璁剧疆涓簃axHeight
+				//瑙e喅褰搖-index-list缁勪欢鏀惧湪tabbar椤甸潰鏃�,scroll-view鍐呭杈冨皯鏃讹紝杩樿兘婊氬姩
+				this.scrollViewHeight = this.sys.windowHeight - this.customNavHeight
+			},
+			// 绱㈠紩鍒楄〃琚Е鎽�
+			touchStart(e) {
+				// 鑾峰彇瑙︽懜鐐逛俊鎭�
+				const touchStart = e.changedTouches[0]
+				if (!touchStart) return
+				this.touching = true
+				const {
+					pageY
+				} = touchStart
+				// 鏍规嵁褰撳墠瑙︽懜鐐圭殑鍧愭爣锛岃幏鍙栧綋鍓嶈Е鎽哥殑涓虹鍑犱釜瀛楁瘝
+				const currentIndex = this.getIndexListLetter(pageY)
+				this.setValueForTouch(currentIndex)
+			},
+			// 绱㈠紩瀛楁瘝鍒楄〃琚Е鎽告粦鍔ㄤ腑
+			touchMove(e) {
+				// 鑾峰彇瑙︽懜鐐逛俊鎭�
+				let touchMove = e.changedTouches[0]
+				if (!touchMove) return;
+
+				// 婊戝姩缁撴潫鍚庤繀閫熷紑濮嬬浜屾婊戝姩鏃跺�� touching 涓� false 閫犳垚涓嶆樉绀� indicator 闂
+				if (!this.touching) {
+					this.touching = true
+				}
+				const {
+					pageY
+				} = touchMove
+				const currentIndex = this.getIndexListLetter(pageY)
+				this.setValueForTouch(currentIndex)
+			},
+			// 瑙︽懜缁撴潫
+			touchEnd(e) {
+				// 寤舵椂涓�瀹氭椂闂村悗鍐嶉殣钘忔寚绀哄櫒锛屼负浜嗚鐢ㄦ埛鐪嬬殑鏇寸洿瑙傦紝鍚屾椂涔熸槸涓轰簡娑堥櫎蹇�熷垏鎹-transition鐨剆how甯︽潵鐨勫奖鍝�
+				uni.$u.sleep(300).then(() => {
+					this.touching = false
+				})
+			},
+			// 鑾峰彇绱㈠紩鍒楄〃鐨勫昂瀵镐互鍙婂崟涓瓧绗︾殑灏哄淇℃伅
+			getIndexListLetterRect() {
+				return new Promise(resolve => {
+					// 寤舵椂涓�瀹氭椂闂达紝浠ヨ幏鍙杁om灏哄
+					// #ifndef APP-NVUE
+					this.$uGetRect('.u-index-list__letter').then(size => {
+						resolve(size)
+					})
+					// #endif
+
+					// #ifdef APP-NVUE
+					const ref = this.$refs['u-index-list__letter']
+					dom.getComponentRect(ref, res => {
+						resolve(res.size)
+					})
+					// #endif
+				})
+			},
+			// 璁剧疆indexList绱㈠紩鐨勫昂瀵镐俊鎭�
+			setIndexListLetterInfo() {
+				this.getIndexListLetterRect().then(size => {
+					const {
+						height
+					} = size
+					const sys = uni.$u.sys()
+					const windowHeight = sys.windowHeight
+					let customNavHeight = 0
+					// 娑堥櫎鍚勭瀵艰埅鏍忛潪鍘熺敓鍜屽師鐢熷鑷寸殑宸紓锛岃绱㈠紩鍒楄〃瀛楁瘝瀵瑰睆骞曞瀭鐩村眳涓�
+					if (this.customNavHeight == 0) {
+						// #ifdef H5
+						customNavHeight = sys.windowTop
+						// #endif
+						// #ifndef H5
+						// 鍦ㄩ潪H5涓紝涓哄師鐢熷鑸爮锛屽叾楂樺害涓嶇畻鍦╳indowHeight鍐咃紝杩欓噷璁剧疆涓鸿礋鍊硷紝鍚庨潰鐩稿姞鏃跺彉鎴愬噺鍘诲叾楂樺害鐨勪竴鍗�
+						customNavHeight = -(sys.statusBarHeight + 44)
+						// #endif
+					} else {
+						customNavHeight = uni.$u.getPx(this.customNavHeight)
+					}
+					this.letterInfo = {
+						height,
+						// 涓轰簡璁╁瓧姣嶅垪琛ㄥ灞忓箷缁濆灞呬腑锛岃鍏跺瀵艰埅鏍忚繘琛屼慨姝o紝涔熷嵆寰�涓婂亸绉诲鑸爮鐨勪竴鍗婇珮搴�
+						top: (windowHeight - height) / 2 + customNavHeight / 2,
+						itemHeight: Math.floor(height / this.uIndexList.length)
+					}
+				})
+			},
+			// 鑾峰彇褰撳墠琚Е鎽哥殑绱㈠紩瀛楁瘝
+			getIndexListLetter(pageY) {
+				const {
+					top,
+					height,
+					itemHeight
+				} = this.letterInfo
+				// 瀵笻5鐨刾ageY杩涜淇锛岃繖鏄敱浜巙ni-app鑷綔澶氭儏鍦℉5涓皢瑙︽懜鐐圭殑鍧愭爣璺烪5鐨勫鑸爮缁撳悎瀵艰嚧鐨勯棶棰�
+				// #ifdef H5
+				pageY += uni.$u.sys().windowTop
+				// #endif
+				// 瀵圭涓�鍜屾渶鍚庝竴涓瓧姣嶅仛杈圭晫澶勭悊锛屽洜涓虹敤鎴峰彲鑳藉湪瀛楁瘝鍒楄〃涓婅Е鎽稿埌涓ょ鐨勫敖澶村悗渚濈劧缁х画婊戝姩
+				if (pageY < top) {
+					return 0
+				} else if (pageY >= top + height) {
+					// 濡傛灉瓒呭嚭浜嗭紝鍙栨渶鍚庝竴涓瓧姣�
+					return this.uIndexList.length - 1
+				} else {
+					// 灏嗚Е鎽哥偣鐨刌杞村亸绉诲�硷紝鍑忓幓绱㈠紩瀛楁瘝鐨則op鍊硷紝闄や互姣忎釜瀛楁瘝鐨勯珮搴︼紝鍗冲彲寰楀埌褰撳墠瑙︽懜鐐硅惤鍦ㄥ摢涓瓧姣嶄笂
+					return Math.floor((pageY - top) / itemHeight);
+				}
+			},
+			// 璁剧疆鍚勯」鐢辫Е鎽歌�屽鑷村彉鍖栫殑鍊�
+			setValueForTouch(currentIndex) {
+				// 濡傛灉鍋忕Щ閲忓お灏忥紝鍓嶅悗寰楀嚭鐨勪細鏄悓涓�涓储寮曞瓧姣嶏紝涓轰簡闃叉姈锛岃繘琛岃繑鍥�
+				if (currentIndex === this.activeIndex) return
+				this.activeIndex = currentIndex
+				// #ifndef APP-NVUE || MP-WEIXIN
+				// 鍦ㄩ潪nvue涓紝鐢变簬anchor鍜宨tem閮藉湪u-index-item涓紝鎵�浠ラ渶瑕佸index-item杩涜鍋忕Щ
+				this.scrollIntoView = `u-index-item-${this.uIndexList[currentIndex].charCodeAt(0)}`
+				// #endif
+				// #ifdef MP-WEIXIN
+				// 寰俊灏忕▼搴忎笅锛宻croll-view鐨剆croll-into-view灞炴�ф棤娉曞slot涓殑鍐呭鐨刬d鐢熸晥锛屽彧鑳介�氳繃璁剧疆scrollTop鐨勫舰寮忓幓绉诲姩婊氬姩鏉�
+				this.scrollTop = this.children[currentIndex].top
+				// #endif
+				// #ifdef APP-NVUE
+				// 鍦╪vue涓紝鐢变簬cell鍜宧eader涓哄悓绾у厓绱狅紝鎵�浠ュ疄闄呮槸闇�瑕佸header(anchor)杩涜鍋忕Щ
+				const anchor = `u-index-anchor-${this.uIndexList[currentIndex]}`
+				dom.scrollToElement(this.anchors[currentIndex].$refs[anchor], {
+					offset: 0,
+					animated: false
+				})
+				// #endif
+			},
+			getHeaderRect() {
+				// 鑾峰彇header slot鐨勯珮搴︼紝鍥犱负list缁勪欢涓幏鍙栧厓绱犵殑灏哄鏄病鏈塼op鍊肩殑
+				return new Promise(resolve => {
+					dom.getComponentRect(this.$refs.header, res => {
+						resolve(res.size)
+					})
+				})
+			},
+			// scroll-view鐨勬粴鍔ㄤ簨浠�
+			async scrollHandler(e) {
+				if (this.touching || this.scrolling) return
+				// 姣忚繃涓�瀹氭椂闂村彇鏍蜂竴娆★紝鍑忓皯璧勬簮鎹熻�椾互鍙婂彲鑳藉甫鏉ョ殑鍗¢】
+				this.scrolling = true
+				uni.$u.sleep(10).then(() => {
+					this.scrolling = false
+				})
+				let scrollTop = 0
+				const len = this.children.length
+				let children = this.children
+				const anchors = this.anchors
+				// #ifdef APP-NVUE
+				// nvue涓嬭幏鍙栫殑婊氬姩鏉″亸绉讳负璐熸暟锛岄渶瑕佽浆涓烘鏁�
+				scrollTop = Math.abs(e.contentOffset.y)
+				// 鑾峰彇header slot鐨勫昂瀵镐俊鎭�
+				const header = await this.getHeaderRect()
+				// item鐨則op鍊硷紝鍦╪vue涓嬶紝妯℃嫙鍑虹殑anchor鐨則op锛岀被浼奸潪nvue涓嬬殑index-item鐨則op
+				let top = header.height
+				// 鐢变簬list缁勪欢鏃犳硶鑾峰彇cell鐨則op鍊硷紝杩欓噷閫氳繃header slot鍜屽悇涓猧tem涔嬮棿鐨刪eight锛屾ā鎷熷嚭绫讳技闈瀗vue涓嬬殑浣嶇疆淇℃伅
+				children = this.children.map((item, index) => {
+					const child = {
+						height: item.height,
+						top
+					}
+					// 杩涜绱姞锛岀粰涓嬩竴涓猧tem鎻愪緵璁$畻渚濇嵁
+					top += item.height + anchors[index].height
+					return child
+				})
+				// #endif
+				// #ifndef APP-NVUE
+				// 闈瀗vue閫氳繃detail鑾峰彇婊氬姩鏉′綅绉�
+				scrollTop = e.detail.scrollTop
+				// #endif
+				for (let i = 0; i < len; i++) {
+					const item = children[i],
+						nextItem = children[i + 1]
+					// 濡傛灉婊氬姩鏉¢珮搴﹀皬浜庣涓�涓猧tem鐨則op鍊硷紝姝ゆ椂鏃犻渶璁剧疆浠绘剰瀛楁瘝涓洪珮浜�
+					if (scrollTop <= children[0].top || scrollTop >= children[len - 1].top + children[len -
+							1].height) {
+						this.activeIndex = -1
+						break
+					} else if (!nextItem) { 
+						// 褰撲笉瀛樺湪涓嬩竴涓猧tem鏃讹紝鎰忓懗鐫�鍘嗛亶鍒颁簡鏈�鍚庝竴涓�
+						this.activeIndex = len - 1
+						break
+					} else if (scrollTop > item.top && scrollTop < nextItem.top) {
+						this.activeIndex = i
+						break
+					}
+				}
+			},
+		},
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+
+	.u-index-list {
+
+		&__letter {
+			position: fixed;
+			right: 0;
+			text-align: center;
+			z-index: 3;
+			padding: 0 6px;
+
+			&__item {
+				width: 16px;
+				height: 16px;
+				border-radius: 100px;
+				margin: 1px 0;
+				@include flex;
+				align-items: center;
+				justify-content: center;
+
+				&--active {
+					background-color: $u-primary;
+				}
+
+				&__index {
+					font-size: 12px;
+					text-align: center;
+					line-height: 12px;
+				}
+			}
+		}
+
+		&__indicator {
+			width: 50px;
+			height: 50px;
+			border-radius: 100px 100px 0 100px;
+			text-align: center;
+			color: #ffffff;
+			background-color: #c9c9c9;
+			transform: rotate(-45deg);
+			@include flex;
+			justify-content: center;
+			align-items: center;
+
+			&__text {
+				font-size: 28px;
+				line-height: 28px;
+				font-weight: bold;
+				color: #fff;
+				transform: rotate(45deg);
+				text-align: center;
+			}
+		}
+	}
+</style>

--
Gitblit v1.9.3