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

diff --git a/uview-ui/components/u-tabs-swiper/u-tabs-swiper.vue b/uview-ui/components/u-tabs-swiper/u-tabs-swiper.vue
new file mode 100644
index 0000000..6b88a85
--- /dev/null
+++ b/uview-ui/components/u-tabs-swiper/u-tabs-swiper.vue
@@ -0,0 +1,488 @@
+<template>
+	<view class="u-tabs" :style="{
+			zIndex: zIndex,
+			background: bgColor
+		}">
+		<scroll-view scroll-x class="u-scroll-view" :scroll-left="scrollLeft" scroll-with-animation :style="{ zIndex: zIndex + 1 }">
+			<view class="u-tabs-scroll-box" :class="{'u-tabs-scorll-flex': !isScroll}">
+				<view class="u-tabs-item" :style="[tabItemStyle(index)]"
+				 v-for="(item, index) in getTabs" :key="index" :class="[preId + index]" @tap="emit(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-scroll-bar" :style="[tabBarStyle]"></view>
+			</view>
+		</scroll-view>
+	</view>
+</template>
+
+<script>
+	import colorGradient from '../../libs/function/colorGradient';
+	let color = colorGradient;
+	const { windowWidth } = uni.getSystemInfoSync();
+	const preId = 'UEl_';
+
+	/**
+	 * tabsSwiper 鍏ㄥ睆閫夐」鍗�
+	 * @description 璇ョ粍浠跺唴閮ㄥ疄鐜颁富瑕佷緷鎵樹簬uniapp鐨剆croll-view鍜宻wiper缁勪欢锛屼富瑕佺壒鑹叉槸鍒囨崲杩囩▼涓紝tabsSwiper鏂囧瓧鐨勯鑹插彲浠ユ笎鍙橈紝搴曢儴婊戝潡鍙互 璺熼殢寮忔粦鍔紝娲诲姩tab婊氬姩灞呬腑绛夈�傚簲鐢ㄥ満鏅彲浠ョ敤浜庨渶瑕佸乏鍙冲垏鎹㈤〉闈紝姣斿鍟嗗煄鐨勮鍗曚腑蹇�(寰呮敹璐�-寰呬粯娆�-寰呰瘎浠�-宸查��璐�)绛夊簲鐢ㄥ満鏅��
+	 * @tutorial https://www.uviewui.com/components/tabsSwiper.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} swiper-width tabs缁勪欢澶栭儴swiper鐨勫搴︼紝榛樿涓哄睆骞曞搴︼紝鍗曚綅rpx锛堥粯璁�750锛�
+	 * @property {String} active-color 婊戝潡鍜屾縺娲籺ab鏂囧瓧鐨勯鑹诧紙榛樿#497bff锛�
+	 * @property {String} inactive-color tabs鏂囧瓧棰滆壊锛堥粯璁�#303133锛�
+	 * @property {String Number} bar-width 婊戝潡瀹藉害锛屽崟浣峳px锛堥粯璁�40锛�
+	 * @property {String Number} bar-height 婊戝潡楂樺害锛屽崟浣峳px锛堥粯璁�6锛�
+	 * @property {Object} bar-style 搴曢儴婊戝潡鐨勬牱寮忥紝瀵硅薄褰㈠紡
+	 * @property {Object} active-item-style 娲诲姩tabs item鐨勬牱寮忥紝瀵硅薄褰㈠紡
+	 * @property {Boolean} show-bar 鏄惁鏄剧ず搴曢儴鐨勬粦鍧楋紙榛樿true锛�
+	 * @property {String Number} gutter 鍗曚釜tab鏍囩鐨勫乏鍙冲唴杈硅窛涔嬪拰锛屽崟浣峳px锛堥粯璁�40锛�
+	 * @property {String} bg-color tabs瀵艰埅鏍忕殑鑳屾櫙棰滆壊锛堥粯璁�#ffffff锛�
+	 * @property {String} name 缁勪欢鍐呴儴璇诲彇鐨刲ist鍙傛暟涓殑灞炴�у悕锛岃瀹樼綉璇存槑锛堥粯璁ame锛�
+	 * @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-swiper ref="tabs" :list="list" :is-scroll="false"></u-tabs-swiper>
+	 */
+	export default {
+		name: "u-tabs-swiper",
+		props: {
+			// 瀵艰埅鑿滃崟鏄惁闇�瑕佹粴鍔紝濡傚彧鏈�2鎴栬��3涓殑鏃跺�欙紝灏变笉闇�瑕佹粴鍔ㄤ簡锛屾鏃朵娇鐢╢lex骞冲垎tab鐨勫搴�
+			isScroll: {
+				type: Boolean,
+				default: true
+			},
+			//闇�寰幆鐨勬爣绛惧垪琛�
+			list: {
+				type: Array,
+				default () {
+					return [];
+				}
+			},
+			// 褰撳墠娲诲姩tab鐨勭储寮�
+			current: {
+				type: [Number, String],
+				default: 0
+			},
+			// 瀵艰埅鏍忕殑楂樺害鍜岃楂橈紝鍗曚綅rpx
+			height: {
+				type: [Number, String],
+				default: 80
+			},
+			// 瀛椾綋澶у皬锛屽崟浣峳px
+			fontSize: {
+				type: [Number, String],
+				default: 30
+			},
+			// 杩囨浮鍔ㄧ敾鏃堕暱, 鍗曚綅s
+			// duration: {
+			// 	type: [Number, String],
+			// 	default: 0.5
+			// },
+			swiperWidth: {
+				//line3鐢熸晥, 澶栭儴swiper鐨勫搴�, 鍗曚綅rpx
+				type: [String, Number],
+				default: 750
+			},
+			// 閫変腑椤圭殑涓婚棰滆壊
+			activeColor: {
+				type: String,
+				default: '#497bff'
+			},
+			// 鏈�変腑椤圭殑棰滆壊
+			inactiveColor: {
+				type: String,
+				default: '#303133'
+			},
+			// 鑿滃崟搴曢儴绉诲姩鐨刡ar鐨勫搴︼紝鍗曚綅rpx
+			barWidth: {
+				type: [Number, String],
+				default: 40
+			},
+			// 绉诲姩bar鐨勯珮搴�
+			barHeight: {
+				type: [Number, String],
+				default: 6
+			},
+			// 鍗曚釜tab鐨勫乏鎴栧彸鍐呰竟璺濓紙鍚勫崰涓�鍗婏級锛屽崟浣峳px
+			gutter: {
+				type: [Number, String],
+				default: 40
+			},
+			// 濡傛灉鏄粷瀵瑰畾浣嶏紝娣诲姞z-index鍊�
+			zIndex: {
+				type: [Number, String],
+				default: 1
+			},
+			// 瀵艰埅鏍忕殑鑳屾櫙棰滆壊
+			bgColor: {
+				type: String,
+				default: '#ffffff'
+			},
+			//婊氬姩鑷充腑蹇冪洰鏍囩被鍨�
+			autoCenterMode: {
+				type: String,
+				default: 'window'
+			},
+			// 璇诲彇浼犲叆鐨勬暟缁勫璞$殑灞炴��(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 {}
+				}
+			}
+		},
+		data() {
+			return {
+				scrollLeft: 0, // 婊氬姩scroll-view鐨勫乏杈规粴鍔ㄨ窛绂�
+				tabQueryInfo: [], // 瀛樻斁瀵箃ab鑿滃崟鏌ヨ鍚庣殑鑺傜偣淇℃伅
+				windowWidth: 0, // 灞忓箷瀹藉害锛屽崟浣嶄负px
+				//scrollBarLeft: 0, // 绉诲姩bar闇�瑕侀�氳繃translateX()绉诲姩鐨勮窛绂�
+				animationFinishCurrent: this.current,
+				componentsWidth: 0,
+				line3AddDx: 0,
+				line3Dx: 0,
+				preId,
+				sW: 0,
+				tabsInfo: [],
+				colorGradientArr: [],
+				colorStep: 100 // 涓や釜棰滆壊涔嬮棿鐨勬笎鍙樼瓑鍒�
+			};
+		},
+		computed: {
+			// 鑾峰彇褰撳墠娲昏穬鐨刢urrent鍊�
+			getCurrent() {
+				const current = Number(this.current);
+				// 鍒ゆ柇鏄惁瓒呭嚭杈圭晫
+				if (current > this.getTabs.length - 1) {
+					return this.getTabs.length - 1;
+				}
+				if (current < 0) return 0;
+				return current;
+			},
+			getTabs() {
+				return this.list;
+			},
+			// 婊戝潡闇�瑕佺Щ鍔ㄧ殑璺濈
+			scrollBarLeft() {
+				return Number(this.line3Dx) + Number(this.line3AddDx);
+			},
+			// 婊戝潡鐨勫搴﹁浆涓簆x鍗曚綅
+			barWidthPx() {
+				return uni.upx2px(this.barWidth);
+			},
+			// tab鐨勬牱寮�
+			tabItemStyle() {
+				return (index) => {
+					let style = {
+						height: this.height + 'rpx',
+						lineHeight: this.height + 'rpx',
+						padding: `0 ${this.gutter / 2}rpx`,
+						color: this.tabsInfo.length > 0 ? (this.tabsInfo[index] ? this.tabsInfo[index].color : this.activeColor) : this.inactiveColor,
+						fontSize: this.fontSize + 'rpx',
+						zIndex: this.zIndex + 2,
+						fontWeight: (index == this.getCurrent && this.bold) ? 'bold' : 'normal'
+					};
+					if(index == this.getCurrent) {
+						// 缁欓�変腑鐨則ab item娣诲姞澶栭儴鑷畾涔夌殑鏍峰紡
+						style = Object.assign(style, this.activeItemStyle);
+					}
+					return style;
+				}
+			},
+			// 搴曢儴婊戝潡鐨勬牱寮�
+			tabBarStyle() {
+				let style = {
+					width: this.barWidthPx + 'px',
+					height: this.barHeight + 'rpx',
+					borderRadius: '100px',
+					backgroundColor: this.activeColor,
+					left: this.scrollBarLeft + 'px'
+				};
+				return Object.assign(style, this.barStyle);
+			}
+		},
+		watch: {
+			current(n, o) {
+				this.change(n);
+				this.setFinishCurrent(n);
+			},
+			list() {
+				this.$nextTick(() => {
+					this.init();
+				})
+			}
+		},
+		mounted() {
+			this.init();
+		},
+		methods: {
+			async init() {
+				this.countPx();
+				await this.getTabsInfo();
+				this.countLine3Dx();
+				this.getQuery(() => {
+					this.setScrollViewToCenter();
+				});
+				// 棰滆壊娓愬彉杩囩▼鏁扮粍
+				this.colorGradientArr = color.colorGradient(this.inactiveColor, this.activeColor, this.colorStep);
+			},
+			// 鑾峰彇鍚勪釜tab鐨勮妭鐐逛俊鎭�
+			getTabsInfo() {
+				return new Promise((resolve, reject) => {
+					let view = uni.createSelectorQuery().in(this);
+					for (let i = 0; i < this.list.length; i++) {
+						view.select('.' + preId + i).boundingClientRect();
+					}
+					view.exec(res => {
+						const arr = [];
+						for (let i = 0; i < res.length; i++) {
+							// 缁欐瘡涓猼ab娣诲姞鍏舵枃瀛楅鑹插睘鎬�
+							res[i].color = this.inactiveColor;
+							// 褰撳墠tab鐩存帴璧嬩簣activeColor
+							if (i == this.getCurrent) res[i].color = this.activeColor;
+							arr.push(res[i]);
+						}
+						this.tabsInfo = arr;
+						resolve();
+					});
+				})
+			},
+			// 褰搒wiper婊戝姩缁撴潫锛岃绠楁粦鍧楁渶缁堣鍋滅暀鐨勪綅缃�
+			countLine3Dx() {
+				const tab = this.tabsInfo[this.animationFinishCurrent];
+				// 璁╂粦鍧椾腑蹇冪偣鍜屽綋鍓峵ab涓績閲嶅悎
+				if (tab) this.line3Dx = tab.left + tab.width / 2 - this.barWidthPx / 2 - this.tabsInfo[0].left;
+			},
+			countPx() {
+				// swiper瀹藉害鐢眗px杞负px鍗曚綅锛屽洜涓篸x绛夛紝閮芥槸px鍗曚綅
+				this.sW = uni.upx2px(Number(this.swiperWidth));
+			},
+			emit(index) {
+				this.$emit('change', index);
+			},
+			change() {
+				this.setScrollViewToCenter();
+			},
+			getQuery(cb) {
+				try {
+					let view = uni.createSelectorQuery().in(this).select('.u-tabs');
+					view.fields({
+							size: true
+						},
+						data => {
+							if (data) {
+								this.componentsWidth = data.width;
+								if (cb && typeof cb === 'function') cb(data);
+							} else {
+								this.getQuery(cb);
+							}
+						}
+					).exec();
+				} catch (e) {
+					this.componentsWidth = windowWidth;
+				}
+			},
+			// 鎶婃椿鍔╰ab绉诲姩鍒板睆骞曚腑蹇冪偣
+			setScrollViewToCenter() {
+				let tab;
+				tab = this.tabsInfo[this.animationFinishCurrent];
+				if (tab) {
+					let tabCenter = tab.left + tab.width / 2;
+					let fatherWidth;
+					// 娲诲姩tab绉诲姩鍒颁腑蹇冩椂锛屼互灞忓箷杩樻槸tab缁勪欢涓哄搴︿负鍩哄噯
+					if (this.autoCenterMode === 'window') {
+						fatherWidth = windowWidth;
+					} else {
+						fatherWidth = this.componentsWidth;
+					}
+					this.scrollLeft = tabCenter - fatherWidth / 2;
+				}
+			},
+			setDx(dx) {
+				let nextTabIndex = dx > 0 ? this.animationFinishCurrent + 1 : this.animationFinishCurrent - 1;
+				// 鍒ゆ柇绱㈠紩鏄惁瓒呭嚭杈圭晫
+				nextTabIndex = nextTabIndex <= 0 ? 0 : nextTabIndex;
+				nextTabIndex = nextTabIndex >= this.list.length ? this.list.length - 1 : nextTabIndex;
+				const tab = this.tabsInfo[nextTabIndex];
+				// 褰撳墠tab涓績鐐箈杞村潗鏍�
+				let nowTab = this.tabsInfo[this.animationFinishCurrent];
+				let nowTabX = nowTab.left + nowTab.width / 2;
+				// 涓嬩竴涓猼ab
+				let nextTab = this.tabsInfo[nextTabIndex];
+				let nextTabX = nextTab.left + nextTab.width / 2;
+				// 涓や釜tab涔嬮棿鐨勮窛绂伙紝鍥犱负涓嬩竴涓猼ab鍙兘鍦ㄥ綋鍓峵ab鐨勫乏杈规垨鑰呭彸杈癸紝鍙栫粷瀵瑰�煎嵆鍙�
+				let distanceX = Math.abs(nextTabX - nowTabX);
+				this.line3AddDx = (dx / this.sW) * distanceX;
+				this.setTabColor(this.animationFinishCurrent, nextTabIndex, dx);
+			},
+			// 璁剧疆tab鐨勯鑹�
+			setTabColor(nowTabIndex, nextTabIndex, dx) {
+				let colorIndex = Math.abs(Math.ceil((dx / this.sW) * 100));
+				let colorLength = this.colorGradientArr.length;
+				// 澶勭悊瓒呭嚭绱㈠紩杈圭晫鐨勬儏鍐�
+				colorIndex = colorIndex >= colorLength ? colorLength - 1 : colorIndex <= 0 ? 0 : colorIndex;
+				// 璁剧疆涓嬩竴涓猼ab鐨勯鑹�
+				this.tabsInfo[nextTabIndex].color = this.colorGradientArr[colorIndex];
+				// 璁剧疆褰撳墠tab鐨勯鑹�
+				this.tabsInfo[nowTabIndex].color = this.colorGradientArr[colorLength - 1 - colorIndex];
+			},
+			// swiper缁撴潫婊戝姩
+			setFinishCurrent(current) {
+				// 濡傛灉婊戝姩鐨勭储寮曚笉涓�鑷达紝淇敼tab棰滆壊鍙樺寲锛屽洜涓哄彲鑳戒細鏈夌洿鎺ョ偣鍑籺ab鐨勬儏鍐�
+				this.tabsInfo.map((val, index) => {
+					if (current == index) val.color = this.activeColor;
+					else val.color = this.inactiveColor;
+					return val;
+				});
+				this.line3AddDx = 0;
+				this.animationFinishCurrent = current;
+				this.countLine3Dx();
+			}
+		}
+	};
+</script>
+
+<style scoped lang="scss">
+	@import "../../libs/css/style.components.scss";
+
+	view,
+	scroll-view {
+		box-sizing: border-box;
+	}
+
+	.u-tabs {
+		width: 100%;
+		transition-property: background-color, color;
+	}
+
+	/* #ifndef APP-NVUE */
+	::-webkit-scrollbar,
+	::-webkit-scrollbar,
+	::-webkit-scrollbar {
+		display: none;
+		width: 0 !important;
+		height: 0 !important;
+		-webkit-appearance: none;
+		background: transparent;
+	}
+	/* #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-tabs-scroll-box {
+		position: relative;
+	}
+
+	.u-tabs-scorll-flex {
+		@include vue-flex;
+		justify-content: space-between;
+	}
+
+	.u-tabs-scorll-flex .u-tabs-item {
+		flex: 1;
+	}
+
+	.u-tabs-item {
+		position: relative;
+		display: inline-block;
+		text-align: center;
+		transition-property: background-color, color, font-weight;
+	}
+
+	.content {
+		overflow: hidden;
+		white-space: nowrap;
+		text-overflow: ellipsis;
+	}
+
+	.boxStyle {
+		pointer-events: none;
+		position: absolute;
+		transition-property: all;
+	}
+
+	.boxStyle2 {
+		pointer-events: none;
+		position: absolute;
+		bottom: 0;
+		transition-property: all;
+		transform: translateY(-100%);
+	}
+
+	.itemBackgroundBox {
+		pointer-events: none;
+		position: absolute;
+		top: 0;
+		transition-property: left, background-color;
+		@include vue-flex;
+		flex-direction: row;
+		justify-content: center;
+		align-items: center;
+	}
+
+	.itemBackground {
+		height: 100%;
+		width: 100%;
+		transition-property: all;
+	}
+
+	.u-scroll-bar {
+		position: absolute;
+		bottom: 4rpx;
+	}
+</style>

--
Gitblit v1.9.3