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

diff --git a/uview-ui/components/u-tabs/u-tabs.vue b/uview-ui/components/u-tabs/u-tabs.vue
new file mode 100644
index 0000000..8762e98
--- /dev/null
+++ b/uview-ui/components/u-tabs/u-tabs.vue
@@ -0,0 +1,369 @@
+<template>
+	<view class="u-tabs" :style="{
+		background: bgColor
+	}">
+		<!-- $u.getRect()瀵圭粍浠舵牴鑺傜偣鏃犳晥锛屽洜涓哄啓浜�.in(this)锛屾晠杩欓噷鑾峰彇鍐呭眰鎺ョ偣灏哄 -->
+		<view :id="id">
+			<scroll-view scroll-x class="u-scroll-view" :scroll-left="scrollLeft" scroll-with-animation>
+				<view class="u-scroll-box" :class="{'u-tabs-scorll-flex': !isScroll}">
+					<view class="u-tab-item u-line-1" :id="'u-tab-item-' + index" v-for="(item, index) in list" :key="index" @tap="clickTab(index)"
+					 :style="[tabItemStyle(index)]">
+						<u-badge :count="item[count] || item['count'] || 0" :offset="offset" size="mini"></u-badge>
+						{{ item[name] || item['name']}}
+					</view>
+					<view v-if="showBar" class="u-tab-bar" :style="[tabBarStyle]"></view>
+				</view>
+			</scroll-view>
+		</view>
+	</view>
+</template>
+
+<script>
+	/**
+	 * tabs 鏍囩
+	 * @description 璇ョ粍浠讹紝鏄竴涓猼abs鏍囩缁勪欢锛屽湪鏍囩澶氱殑鏃跺�欙紝鍙互閰嶇疆涓哄乏鍙虫粦鍔紝鏍囩灏戠殑鏃跺�欙紝鍙互绂佹婊戝姩銆� 璇ョ粍浠剁殑涓�涓壒鐐规槸閰嶇疆涓烘粴鍔ㄦā寮忔椂锛屾縺娲荤殑tab浼氳嚜鍔ㄧЩ鍔ㄥ埌缁勪欢鐨勪腑闂翠綅缃��
+	 * @tutorial https://www.uviewui.com/components/tabs.html
+	 * @property {Boolean} is-scroll tabs鏄惁鍙互宸﹀彸鎷栧姩锛堥粯璁rue锛�
+	 * @property {Array} list 鏍囩鏁扮粍锛屽厓绱犱负瀵硅薄锛屽[{name: '鎺ㄨ崘'}]
+	 * @property {String Number} current 鎸囧畾鍝釜tab涓烘縺娲荤姸鎬侊紙榛樿0锛�
+	 * @property {String Number} height 瀵艰埅鏍忕殑楂樺害锛屽崟浣峳px锛堥粯璁�80锛�
+	 * @property {String Number} font-size tab鏂囧瓧澶у皬锛屽崟浣峳px锛堥粯璁�30锛�
+	 * @property {String Number} duration 婊戝潡绉诲姩涓�娆℃墍闇�鐨勬椂闂达紝鍗曚綅绉掞紙榛樿0.5锛�
+	 * @property {String} active-color 婊戝潡鍜屾縺娲籺ab鏂囧瓧鐨勯鑹诧紙榛樿#497bff锛�
+	 * @property {String} inactive-color tabs鏂囧瓧棰滆壊锛堥粯璁�#303133锛�
+	 * @property {String Number} bar-width 婊戝潡瀹藉害锛屽崟浣峳px锛堥粯璁�40锛�
+	 * @property {Object} active-item-style 娲诲姩tabs item鐨勬牱寮忥紝瀵硅薄褰㈠紡
+	 * @property {Object} bar-style 搴曢儴婊戝潡鐨勬牱寮忥紝瀵硅薄褰㈠紡
+	 * @property {Boolean} show-bar 鏄惁鏄剧ず搴曢儴鐨勬粦鍧楋紙榛樿true锛�
+	 * @property {String Number} bar-height 婊戝潡楂樺害锛屽崟浣峳px锛堥粯璁�6锛�
+	 * @property {String Number} item-width 鏍囩鐨勫搴︼紙榛樿auto锛�
+	 * @property {String Number} gutter 鍗曚釜tab鏍囩鐨勫乏鍙冲唴杈硅窛涔嬪拰锛屽崟浣峳px锛堥粯璁�40锛�
+	 * @property {String} bg-color tabs瀵艰埅鏍忕殑鑳屾櫙棰滆壊锛堥粯璁�#ffffff锛�
+	 * @property {String} name 缁勪欢鍐呴儴璇诲彇鐨刲ist鍙傛暟涓殑灞炴�у悕锛坱ab鍚嶇О锛夛紝瑙佸畼缃戣鏄庯紙榛樿name锛�
+	 * @property {String} count 缁勪欢鍐呴儴璇诲彇鐨刲ist鍙傛暟涓殑灞炴�у悕锛坆adge寰芥爣鏁帮級锛屽悓name灞炴�х殑浣跨敤锛岃瀹樼綉璇存槑锛堥粯璁ount锛�
+	 * @property {Array} offset 璁剧疆badge寰芥爣鏁扮殑浣嶇疆鍋忕Щ锛屾牸寮忎负 [x, y]锛屼篃鍗宠缃殑涓簍op鍜宺ight鐨勫�硷紝鍗曚綅rpx锛堥粯璁5, 20]锛�
+	 * @property {Boolean} bold 婵�娲婚�夐」鐨勫瓧浣撴槸鍚﹀姞绮楋紙榛樿true锛�
+	 * @event {Function} change 鐐瑰嚮鏍囩鏃惰Е鍙�
+	 * @example <u-tabs ref="tabs" :list="list" :is-scroll="false"></u-tabs>
+	 */
+	export default {
+		name: "u-tabs",
+		props: {
+			// 瀵艰埅鑿滃崟鏄惁闇�瑕佹粴鍔紝濡傚彧鏈�2鎴栬��3涓殑鏃跺�欙紝灏变笉闇�瑕佹粴鍔ㄤ簡锛屾鏃朵娇鐢╢lex骞冲垎tab鐨勫搴�
+			isScroll: {
+				type: Boolean,
+				default: true
+			},
+			//闇�寰幆鐨勬爣绛惧垪琛�
+			list: {
+				type: Array,
+				default () {
+					return [];
+				}
+			},
+			// 褰撳墠娲诲姩tab鐨勭储寮�
+			current: {
+				type: [Number, String],
+				default: 0
+			},
+			// 瀵艰埅鏍忕殑楂樺害鍜岃楂�
+			height: {
+				type: [String, Number],
+				default: 80
+			},
+			// 瀛椾綋澶у皬
+			fontSize: {
+				type: [String, Number],
+				default: 30
+			},
+			// 杩囨浮鍔ㄧ敾鏃堕暱, 鍗曚綅ms
+			duration: {
+				type: [String, Number],
+				default: 0.5
+			},
+			// 閫変腑椤圭殑涓婚棰滆壊
+			activeColor: {
+				type: String,
+				default: '#497bff'
+			},
+			// 鏈�変腑椤圭殑棰滆壊
+			inactiveColor: {
+				type: String,
+				default: '#303133'
+			},
+			// 鑿滃崟搴曢儴绉诲姩鐨刡ar鐨勫搴︼紝鍗曚綅rpx
+			barWidth: {
+				type: [String, Number],
+				default: 40
+			},
+			// 绉诲姩bar鐨勯珮搴�
+			barHeight: {
+				type: [String, Number],
+				default: 6
+			},
+			// 鍗曚釜tab鐨勫乏鎴栨湁鍐呰竟璺濓紙宸﹀彸鐩稿悓锛�
+			gutter: {
+				type: [String, Number],
+				default: 30
+			},
+			// 瀵艰埅鏍忕殑鑳屾櫙棰滆壊
+			bgColor: {
+				type: String,
+				default: '#ffffff'
+			},
+			// 璇诲彇浼犲叆鐨勬暟缁勫璞$殑灞炴��(tab鍚嶇О)
+			name: {
+				type: String,
+				default: 'name'
+			},
+			// 璇诲彇浼犲叆鐨勬暟缁勫璞$殑灞炴��(寰芥爣鏁�)
+			count: {
+				type: String,
+				default: 'count'
+			},
+			// 寰芥爣鏁颁綅缃亸绉�
+			offset: {
+				type: Array,
+				default: () => {
+					return [5, 20]
+				}
+			},
+			// 娲诲姩tab瀛椾綋鏄惁鍔犵矖
+			bold: {
+				type: Boolean,
+				default: true
+			},
+			// 褰撳墠娲诲姩tab item鐨勬牱寮�
+			activeItemStyle: {
+				type: Object,
+				default() {
+					return {}
+				}
+			},
+			// 鏄惁鏄剧ず搴曢儴鐨勬粦鍧�
+			showBar: {
+				type: Boolean,
+				default: true
+			},
+			// 搴曢儴婊戝潡鐨勮嚜瀹氫箟鏍峰紡
+			barStyle: {
+				type: Object,
+				default() {
+					return {}
+				}
+			},
+			// 鏍囩鐨勫搴�
+			itemWidth: {
+				type: [Number, String],
+				default: 'auto'
+			}
+		},
+		data() {
+			return {
+				scrollLeft: 0, // 婊氬姩scroll-view鐨勫乏杈规粴鍔ㄨ窛绂�
+				tabQueryInfo: [], // 瀛樻斁瀵箃ab鑿滃崟鏌ヨ鍚庣殑鑺傜偣淇℃伅
+				componentWidth: 0, // 灞忓箷瀹藉害锛屽崟浣嶄负px
+				scrollBarLeft: 0, // 绉诲姩bar闇�瑕侀�氳繃translateX()绉诲姩鐨勮窛绂�
+				parentLeft: 0, // 鐖跺厓绱�(tabs缁勪欢)鍒板睆骞曞乏杈圭殑璺濈
+				id: this.$u.guid(), // id鍊�
+				currentIndex: this.current,
+				barFirstTimeMove: true, // 婊戝潡绗竴娆$Щ鍔ㄦ椂(椤甸潰鍒氱敓鎴愭椂)锛屾棤闇�鍔ㄧ敾锛屽惁鍒欑粰浜烘�紓鐨勬劅瑙�
+			};
+		},
+		watch: {
+			// 鐩戝惉tab鐨勫彉鍖栵紝閲嶆柊璁$畻tab鑿滃崟鐨勫竷灞�淇℃伅锛屽洜涓哄疄闄呬娇鐢ㄤ腑鑿滃崟鍙兘鏄�氳繃
+			// 鍚庡彴鑾峰彇鐨勶紙濡傛柊闂籥pp椤堕儴鐨勮彍鍗曪級锛岃幏鍙栬繑鍥為渶瑕佷竴瀹氭椂闂达紝鎵�浠ist鍙樺寲鏃讹紝閲嶆柊鑾峰彇甯冨眬淇℃伅
+			list(n, o) {
+				// list鍙樺姩鏃讹紝閲嶅埗鍐呴儴绱㈠紩锛屽惁鍒欏彲鑳藉鑷磋秴鍑烘暟缁勮竟鐣岀殑鎯呭喌
+				if(n.length !== o.length) this.currentIndex = 0;
+				// 鐢�$nextTick绛夊緟瑙嗗浘鏇存柊瀹屾瘯鍚庡啀璁$畻tab鐨勫眬閮ㄤ俊鎭紝鍚﹀垯鍙兘鍥犱负tab杩樻病鐢熸垚灏辫幏鍙栵紝灏变細鏈夐棶棰�
+				this.$nextTick(() => {
+					this.init();
+				});
+			},
+			current: {
+				immediate: true,
+				handler(nVal, oVal) {
+					// 瑙嗗浘鏇存柊鍚庡啀鎵ц绉诲姩鎿嶄綔
+					this.$nextTick(() => {
+						this.currentIndex = nVal;
+						this.scrollByIndex();
+					});
+				}
+			},
+		},
+		computed: {
+			// 绉诲姩bar鐨勬牱寮�
+			tabBarStyle() {
+				let style = {
+					width: this.barWidth + 'rpx',
+					transform: `translate(${this.scrollBarLeft}px, -100%)`,
+					// 婊戝潡鍦ㄩ〉闈㈡覆鏌撳悗绗竴娆℃粦鍔ㄦ椂锛屾棤闇�鍔ㄧ敾鏁堟灉
+					'transition-duration': `${this.barFirstTimeMove ? 0 : this.duration }s`,
+					'background-color': this.activeColor,
+					height: this.barHeight + 'rpx',
+					opacity: this.barFirstTimeMove ? 0 : 1,
+					// 璁剧疆涓�涓緢澶х殑鍊硷紝瀹冧細鑷姩鍙栬兘鐢ㄧ殑鏈�澶у�硷紝涓嶇敤楂樺害鐨勪竴鍗婏紝鏄洜涓洪珮搴﹀彲鑳芥槸鍗曟暟锛屼細鏈夊皬鏁板嚭鐜�
+					'border-radius': `${this.barHeight / 2}px`
+				};
+				Object.assign(style, this.barStyle);
+				return style;
+			},
+			// tab鐨勬牱寮�
+			tabItemStyle() {
+				return (index) => {
+					let style = {
+						height: this.height + 'rpx',
+						'line-height': this.height + 'rpx',
+						'font-size': this.fontSize + 'rpx',
+						'transition-duration': `${this.duration}s`,
+						padding: this.isScroll ? `0 ${this.gutter}rpx` : '',
+						flex: this.isScroll ? 'auto' : '1',
+						width: this.$u.addUnit(this.itemWidth)
+					};
+					// 瀛椾綋鍔犵矖
+					if (index == this.currentIndex && this.bold) style.fontWeight = 'bold';
+					if (index == this.currentIndex) {
+						style.color = this.activeColor;
+						// 缁欓�変腑鐨則ab item娣诲姞澶栭儴鑷畾涔夌殑鏍峰紡
+						style = Object.assign(style, this.activeItemStyle);
+					} else {
+						style.color = this.inactiveColor;
+					}
+					return style;
+				}
+			}
+		},
+		methods: {
+			// 璁剧疆涓�涓猧nit鏂规硶锛屾柟渚垮澶勮皟鐢�
+			async init() {
+				// 鑾峰彇tabs缁勪欢鐨勫昂瀵镐俊鎭�
+				let tabRect = await this.$uGetRect('#' + this.id);
+				// tabs缁勪欢璺濈灞忓箷宸﹁竟鐨勫搴�
+				this.parentLeft = tabRect.left;
+				// tabs缁勪欢鐨勫搴�
+				this.componentWidth = tabRect.width;
+				this.getTabRect();
+			},
+			// 鐐瑰嚮鏌愪竴涓猼ab鑿滃崟
+			clickTab(index) {
+				// 鐐瑰嚮褰撳墠娲诲姩tab锛屼笉瑙﹀彂浜嬩欢
+				if(index == this.currentIndex) return ;
+				// 鍙戦�佷簨浠剁粰鐖剁粍浠�
+				this.$emit('change', index);
+			},
+			// 鏌ヨtab鐨勫竷灞�淇℃伅
+			getTabRect() {
+				// 鍒涘缓鑺傜偣鏌ヨ
+				let query = uni.createSelectorQuery().in(this);
+				// 鍘嗛亶鎵�鏈塼ab锛岃繖閲屾槸鎵ц浜嗘煡璇紝鏈�缁堜娇鐢╡xec()浼氫竴娆℃�ц繑鍥炴煡璇㈢殑鏁扮粍缁撴灉
+				for (let i = 0; i < this.list.length; i++) {
+					// 鍙size鍜宺ect涓や釜鍙傛暟
+					query.select(`#u-tab-item-${i}`).fields({
+						size: true,
+						rect: true
+					});
+				}
+				// 鎵ц鏌ヨ锛屼竴娆℃�ц幏鍙栧涓粨鏋�
+				query.exec(
+					function(res) {
+						this.tabQueryInfo = res;
+						// 鍒濆鍖栨粴鍔ㄦ潯鍜岀Щ鍔╞ar鐨勪綅缃�
+						this.scrollByIndex();
+					}.bind(this)
+				);
+			},
+			// 婊氬姩scroll-view锛岃娲诲姩鐨則ab澶勪簬灞忓箷鐨勪腑闂翠綅缃�
+			scrollByIndex() {
+				// 褰撳墠娲诲姩tab鐨勫竷灞�淇℃伅锛屾湁tab鑿滃崟鐨剋idth鍜宭eft(涓哄厓绱犲乏杈圭晫鍒扮埗鍏冪礌宸﹁竟鐣岀殑璺濈)绛変俊鎭�
+				let tabInfo = this.tabQueryInfo[this.currentIndex];
+				if (!tabInfo) return;
+				// 娲诲姩tab鐨勫搴�
+				let tabWidth = tabInfo.width;
+				// 娲诲姩item鐨勫乏杈瑰埌tabs缁勪欢宸﹁竟鐨勮窛绂伙紝鐢╥tem鐨刲eft鍑忓幓tabs鐨刲eft
+				let offsetLeft = tabInfo.left - this.parentLeft;
+				// 灏嗘椿鍔ㄧ殑tabs-item绉诲姩鍒板睆骞曟涓棿锛屽疄闄呬笂鏄scroll-view鐨勭Щ鍔�
+				let scrollLeft = offsetLeft - (this.componentWidth - tabWidth) / 2;
+				this.scrollLeft = scrollLeft < 0 ? 0 : scrollLeft;
+				// 褰撳墠娲诲姩item鐨勪腑鐐圭偣鍒板乏杈圭殑璺濈鍑忓幓婊戝潡瀹藉害鐨勪竴鍗婏紝鍗冲彲寰楀埌婊戝潡鎵�闇�鐨勭Щ鍔ㄨ窛绂�
+				let left = tabInfo.left + tabInfo.width / 2 - this.parentLeft;
+				// 璁$畻褰撳墠娲昏穬item鍒扮粍浠跺乏杈圭殑璺濈
+				this.scrollBarLeft = left - uni.upx2px(this.barWidth) / 2;
+				// 绗竴娆$Щ鍔ㄦ粦鍧楃殑鏃跺�欙紝barFirstTimeMove涓簍rue锛屾斁鍒板欢鏃朵腑灏嗗叾璁剧疆false
+				// 寤舵椂鏄洜涓簊crollBarLeft浣滅敤浜巆omputed璁$畻鏃讹紝闇�瑕佷竴涓繃绋嬮渶锛屽惁鍒欏鑷村嚭閿�
+				if(this.barFirstTimeMove == true) {
+					setTimeout(() => {
+						this.barFirstTimeMove = false;
+					}, 100)
+				}
+			}
+		},
+		mounted() {
+			this.init();
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/style.components.scss";
+
+	view,
+	scroll-view {
+		box-sizing: border-box;
+	}
+
+	/* #ifndef APP-NVUE */
+	::-webkit-scrollbar,
+	::-webkit-scrollbar,
+	::-webkit-scrollbar {
+		display: none;
+		width: 0 !important;
+		height: 0 !important;
+		-webkit-appearance: none;
+		background: transparent;
+	}
+	/* #endif */
+
+	.u-scroll-box {
+		position: relative;
+		/* #ifdef MP-TOUTIAO */
+		white-space: nowrap;
+		/* #endif */
+	}
+
+	/* #ifdef H5 */
+	// 閫氳繃鏍峰紡绌块�忥紝闅愯棌H5涓嬶紝scroll-view涓嬬殑婊氬姩鏉�
+	scroll-view ::v-deep ::-webkit-scrollbar {
+		display: none;
+		width: 0 !important;
+		height: 0 !important;
+		-webkit-appearance: none;
+		background: transparent;
+	}
+	/* #endif */
+
+	.u-scroll-view {
+		width: 100%;
+		white-space: nowrap;
+		position: relative;
+	}
+
+	.u-tab-item {
+		position: relative;
+		/* #ifndef APP-NVUE */
+		display: inline-block;
+		/* #endif */
+		text-align: center;
+		transition-property: background-color, color;
+	}
+
+	.u-tab-bar {
+		position: absolute;
+		bottom: 0;
+	}
+
+	.u-tabs-scorll-flex {
+		@include vue-flex;
+		justify-content: space-between;
+	}
+</style>

--
Gitblit v1.9.3