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