z-tabs.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721
  1. <!-- z-tabs v0.2.2 by-ZXLee -->
  2. <!-- github地址:https://github.com/SmileZXLee/uni-z-tabs -->
  3. <!-- dcloud地址:https://ext.dcloud.net.cn/plugin?name=z-tabs -->
  4. <!-- 反馈QQ群:790460711 -->
  5. <template name="z-tabs">
  6. <view class="z-tabs-conatiner" :style="[{background:bgColor}, tabsStyle]">
  7. <view class="z-tabs-left">
  8. <slot name="left" />
  9. </view>
  10. <view ref="z-tabs-scroll-view-conatiner" class="z-tabs-scroll-view-conatiner">
  11. <scroll-view ref="z-tabs-scroll-view" class="z-tabs-scroll-view" :scroll-x="true" :scroll-left="scrollLeft" :show-scrollbar="false" :scroll-with-animation="isFirstLoaded" @scroll="scroll">
  12. <view class="z-tabs-list-container" :style="[tabsListStyle]">
  13. <view class="z-tabs-list" :style="[tabsListStyle, {marginTop: -finalBottomSpace+'px'}]">
  14. <view :ref="`z-tabs-item-${index}`" :id="`z-tabs-item-${index}`" class="z-tabs-item" :style="[tabStyle]" v-for="(item,index) in list" :key="index" @click="tabsClick(index,item)">
  15. <view class="z-tabs-item-title-container">
  16. <text :class="{'z-tabs-item-title':true,'z-tabs-item-title-disabled':item.disabled}"
  17. :style="[{color:item.disabled?disabledColor:(currentIndex===index?activeColor:inactiveColor)},item.disabled?disabledStyle:(currentIndex===index?activeStyle:inactiveStyle)]">
  18. {{item[nameKey]||item}}
  19. </text>
  20. <text v-if="item.badge&&_formatCount(item.badge.count).length" class="z-tabs-item-badge" :style="[badgeStyle]">{{_formatCount(item.badge.count)}}</text>
  21. </view>
  22. </view>
  23. </view>
  24. <view class="z-tabs-bottom" :style="[{width: tabsContainerWidth+'px', bottom: finalBottomSpace+'px'}]">
  25. <view ref="z-tabs-bottom-dot" class="z-tabs-bottom-dot"
  26. <!-- #ifndef APP-NVUE -->
  27. :style="[{transform:`translateX(${bottomDotX}px)`,transition:dotTransition,background:activeColor},finalDotStyle]"
  28. <!-- #endif -->
  29. <!-- #ifdef APP-NVUE -->
  30. :style="[{background:activeColor},finalDotStyle]"
  31. <!-- #endif -->
  32. />
  33. </view>
  34. </view>
  35. </scroll-view>
  36. </view>
  37. <view class="z-tabs-right">
  38. <slot name="right" />
  39. </view>
  40. </view>
  41. </template>
  42. <script>
  43. // #ifdef APP-NVUE
  44. const weexDom = weex.requireModule('dom');
  45. const weexAnimation = weex.requireModule('animation');
  46. // #endif
  47. import zTabsConfig from './config/index'
  48. //获取默认配置信息
  49. function _gc(key, defaultValue) {
  50. let config = null;
  51. if (zTabsConfig && Object.keys(zTabsConfig).length) {
  52. config = zTabsConfig;
  53. } else {
  54. return defaultValue;
  55. }
  56. const value = config[_toKebab(key)];
  57. return value === undefined ? defaultValue : value;
  58. }
  59. //驼峰转短横线
  60. function _toKebab(value) {
  61. return value.replace(/([A-Z])/g, "-$1").toLowerCase();
  62. }
  63. /**
  64. * z-tabs 标签
  65. * @description 一个简单轻量的tabs标签,全平台兼容,支持nvue、vue3
  66. * @tutorial https://ext.dcloud.net.cn/plugin?name=z-tabs
  67. * @property {Array} list 数据源数组,支持形如['tab1','tab2']的格式或[{name:'tab1',value:1}]的格式
  68. * @property {Number|String} current 当前选中的index,默认为0
  69. * @property {Number|String} scroll-count list数组长度超过scrollCount时滚动显示(不自动铺满全屏),默认为5
  70. * @property {Number|String} tab-width 自定义每个tab的宽度,默认为0,即代表根据内容自动撑开,单位rpx,支持传100、"100px"或"100rpx"
  71. * @property {Number|String} bar-width 滑块宽度,单位rpx,支持传100、"100px"或"100rpx"
  72. * @property {Number|String} bar-height 滑块高度,单位rpx,支持传100、"100px"或"100rpx"
  73. * @property {Object} bar-style 滑块样式,其中的width和height将被bar-width和bar-height覆盖
  74. * @property {Number|String} bottom-space tabs与底部的间距,单位rpx,支持传100、"100px"或"100rpx"
  75. * @property {String} bar-animate-mode 切换tab时滑块动画模式,与swiper联动时有效,点击切换tab时无效,必须调用setDx。默认为line,即切换tab时滑块宽度保持不变,线性运动。可选值为worm,即为类似毛毛虫蠕动效果
  76. * @property {String} name-key list中item的name(标题)的key,默认为name
  77. * @property {String} value-key list中item的value的key,默认为value
  78. * @property {String} active-color 激活状态tab的颜色
  79. * @property {String} inactive-color 未激活状态tab的颜色
  80. * @property {String} disabled-color 禁用状态tab的颜色
  81. * @property {Object} active-style 激活状态tab的样式
  82. * @property {Object} inactive-style 未激活状态tab的样式
  83. * @property {Object} disabled-style 禁用状态tab的样式
  84. * @property {Number|String} badge-max-count 徽标数最大数字限制,超过这个数字将变成badge-max-count+,默认为99
  85. * @property {Object} badge-style 徽标样式,例如可自定义背景色,字体等等
  86. * @property {String} bg-color z-tabs背景色
  87. * @property {Object} tabs-style z-tabs样式
  88. * @property {Boolean} init-trigger-change 初始化时是否自动触发change事件
  89. * @event {Function(index,value)} change tabs改变时触发,index:当前切换到的index;value:当前切换到的value
  90. * @example <z-tabs :list="list"></z-tabs>
  91. */
  92. export default {
  93. name: 'z-tabs',
  94. data() {
  95. return {
  96. currentIndex: 0,
  97. currentSwiperIndex: 0,
  98. bottomDotX: -1,
  99. bottomDotXForIndex: 0,
  100. showBottomDot: false,
  101. shouldSetDx: true,
  102. barCalcedWidth: 0,
  103. pxBarWidth: 0,
  104. scrollLeft: 0,
  105. tabsSuperWidth: uni.upx2px(750),
  106. tabsWidth: uni.upx2px(750),
  107. tabsHeight: uni.upx2px(80),
  108. tabsLeft: 0,
  109. tabsContainerWidth: 0,
  110. itemNodeInfos: [],
  111. isFirstLoaded: false,
  112. currentScrollLeft: 0,
  113. changeTriggerFailed: false
  114. };
  115. },
  116. props: {
  117. //数据源数组,支持形如['tab1','tab2']的格式或[{name:'tab1',value:1}]的格式
  118. list: {
  119. type: Array,
  120. default: function() {
  121. return [];
  122. }
  123. },
  124. //当前选中的index
  125. current: {
  126. type: [Number, String],
  127. default: _gc('current',0)
  128. },
  129. //list数组长度超过scrollCount时滚动显示(不自动铺满全屏)
  130. scrollCount: {
  131. type: [Number, String],
  132. default: _gc('scrollCount',5)
  133. },
  134. //z-tabs样式
  135. tabsStyle: {
  136. type: Object,
  137. default: function() {
  138. return _gc('tabsStyle',{})
  139. }
  140. },
  141. //自定义每个tab的宽度,默认为0,即代表根据内容自动撑开,单位rpx,支持传100、"100px"或"100rpx"
  142. tabWidth: {
  143. type: [Number, String],
  144. default: _gc('tabWidth',0)
  145. },
  146. //滑块宽度,单位rpx,支持传100、"100px"或"100rpx"
  147. barWidth: {
  148. type: [Number, String],
  149. default: _gc('barWidth',45)
  150. },
  151. //滑块高度,单位rpx,支持传100、"100px"或"100rpx"
  152. barHeight: {
  153. type: [Number, String],
  154. default: _gc('barHeight',8)
  155. },
  156. //滑块样式,其中的width和height将被barWidth和barHeight覆盖
  157. barStyle: {
  158. type: Object,
  159. default: function() {
  160. return _gc('barStyle',{});
  161. }
  162. },
  163. //tabs与底部的间距,单位rpx,支持传100、"100px"或"100rpx"
  164. bottomSpace: {
  165. type: [Number, String],
  166. default: _gc('bottomSpace',8)
  167. },
  168. //切换tab时滑块动画模式,与swiper联动时有效,点击切换tab时无效,必须调用setDx。默认为line,即切换tab时滑块宽度保持不变,线性运动。可选值为worm,即为类似毛毛虫蠕动效果
  169. barAnimateMode: {
  170. type: String,
  171. default: _gc('barAnimateMode','line')
  172. },
  173. //list中item的name(标题)的key
  174. nameKey: {
  175. type: String,
  176. default: _gc('nameKey','name')
  177. },
  178. //list中item的value的key
  179. valueKey: {
  180. type: String,
  181. default: _gc('valueKey','value')
  182. },
  183. //激活状态tab的颜色
  184. activeColor: {
  185. type: String,
  186. default: _gc('activeColor','#007AFF')
  187. },
  188. //未激活状态tab的颜色
  189. inactiveColor: {
  190. type: String,
  191. default: _gc('inactiveColor','#666666')
  192. },
  193. //禁用状态tab的颜色
  194. disabledColor: {
  195. type: String,
  196. default: _gc('disabledColor','#bbbbbb')
  197. },
  198. //激活状态tab的样式
  199. activeStyle: {
  200. type: Object,
  201. default: function() {
  202. return _gc('activeStyle',{});
  203. }
  204. },
  205. //未激活状态tab的样式
  206. inactiveStyle: {
  207. type: Object,
  208. default: function() {
  209. return _gc('inactiveStyle',{});
  210. }
  211. },
  212. //禁用状态tab的样式
  213. disabledStyle: {
  214. type: Object,
  215. default: function() {
  216. return _gc('disabledStyle',{});
  217. }
  218. },
  219. //z-tabs背景色
  220. bgColor: {
  221. type: String,
  222. default: _gc('bgColor','white')
  223. },
  224. //徽标数最大数字限制,超过这个数字将变成badgeMaxCount+
  225. badgeMaxCount: {
  226. type: [Number, String],
  227. default: _gc('badgeMaxCount',99)
  228. },
  229. //徽标样式,例如可自定义背景色,字体等等
  230. badgeStyle: {
  231. type: Object,
  232. default: function() {
  233. return _gc('badgeStyle',{})
  234. }
  235. },
  236. //初始化时是否自动触发change事件
  237. initTriggerChange: {
  238. type: Boolean,
  239. default: _gc('initTriggerChange',false)
  240. },
  241. },
  242. mounted() {
  243. this.updateSubviewLayout();
  244. },
  245. watch: {
  246. current: {
  247. handler(newVal) {
  248. this.currentIndex = newVal;
  249. this._preUpdateDotPosition(this.currentIndex);
  250. if (this.initTriggerChange) {
  251. if (newVal < this.list.length) {
  252. this.$emit('change', newVal, this.list[newVal][this.valueKey]);
  253. }else {
  254. this.changeTriggerFailed = true;
  255. }
  256. }
  257. },
  258. immediate: true
  259. },
  260. list: {
  261. handler(newVal) {
  262. this._handleListChange(newVal);
  263. },
  264. immediate: false
  265. },
  266. bottomDotX(newVal) {
  267. if(newVal >= 0){
  268. // #ifndef APP-NVUE
  269. this.showBottomDot = true;
  270. // #endif
  271. this.$nextTick(() => {
  272. // #ifdef APP-NVUE
  273. weexAnimation.transition(this.$refs['z-tabs-bottom-dot'], {
  274. styles: {
  275. transform: `translateX(${newVal}px)`
  276. },
  277. duration: this.showAnimate ? 200 : 0,
  278. delay: 0
  279. })
  280. setTimeout(() => {
  281. this.showBottomDot = true;
  282. },10)
  283. // #endif
  284. })
  285. }
  286. },
  287. finalBarWidth: {
  288. handler(newVal) {
  289. this.barCalcedWidth = newVal;
  290. this.pxBarWidth = this.barCalcedWidth;
  291. },
  292. immediate: true
  293. },
  294. currentIndex: {
  295. handler(newVal) {
  296. this.currentSwiperIndex = newVal;
  297. },
  298. immediate: true
  299. }
  300. },
  301. computed: {
  302. shouldScroll(){
  303. return this.list.length > this.scrollCount;
  304. },
  305. finalTabsHeight(){
  306. return this.tabsHeight;
  307. },
  308. tabStyle(){
  309. const stl = this.shouldScroll ? {'flex-shrink': 0} : {'flex': 1};
  310. if(this.finalTabWidth > 0){
  311. stl['width'] = this.finalTabWidth + 'px';
  312. }else{
  313. delete stl.width;
  314. }
  315. return stl;
  316. },
  317. tabsListStyle(){
  318. return this.shouldScroll ? {} : {'flex':1};
  319. },
  320. showAnimate(){
  321. return this.isFirstLoaded && !this.shouldSetDx;
  322. },
  323. dotTransition(){
  324. return this.showAnimate ? 'transform .2s linear':'none';
  325. },
  326. finalDotStyle(){
  327. return {...this.barStyle, width: this.barCalcedWidth + 'px', height: this.finalBarHeight + 'px', opacity: this.showBottomDot ? 1 : 0};
  328. },
  329. finalTabWidth(){
  330. return this._convertTextToPx(this.tabWidth);
  331. },
  332. finalBarWidth(){
  333. return this._convertTextToPx(this.barWidth);
  334. },
  335. finalBarHeight(){
  336. return this._convertTextToPx(this.barHeight);
  337. },
  338. finalBottomSpace(){
  339. return this._convertTextToPx(this.bottomSpace);
  340. }
  341. },
  342. methods: {
  343. //根据swiper的@transition实时更新底部dot位置
  344. setDx(dx) {
  345. if (!this.shouldSetDx) return;
  346. const isLineMode = this.barAnimateMode === 'line';
  347. const isWormMode = this.barAnimateMode === 'worm';
  348. let dxRate = dx / this.tabsSuperWidth;
  349. this.currentSwiperIndex = this.currentIndex + parseInt(dxRate);
  350. const isRight = dxRate > 0;
  351. const barWidth = this.pxBarWidth;
  352. if(this.currentSwiperIndex !== this.currentIndex){
  353. dxRate = dxRate - (this.currentSwiperIndex - this.currentIndex);
  354. this.bottomDotXForIndex = this._getBottomDotX(this.itemNodeInfos[this.currentSwiperIndex], barWidth);
  355. }
  356. const currentIndex = this.currentSwiperIndex;
  357. let nextIndex = currentIndex + (isRight ? 1 : -1);
  358. nextIndex = Math.max(0, nextIndex);
  359. nextIndex = Math.min(nextIndex, this.itemNodeInfos.length - 1);
  360. const currentNodeInfo = this.itemNodeInfos[currentIndex];
  361. const nextNodeInfo = this.itemNodeInfos[nextIndex];
  362. const nextBottomX = this._getBottomDotX(nextNodeInfo, barWidth);
  363. if (isLineMode){
  364. this.bottomDotX = this.bottomDotXForIndex + (nextBottomX - this.bottomDotXForIndex) * Math.abs(dxRate);
  365. } else if (isWormMode) {
  366. if ((isRight && currentIndex >= this.itemNodeInfos.length - 1) || (!isRight && currentIndex <= 0)) return;
  367. const spaceOffset = isRight ? nextNodeInfo.right - currentNodeInfo.left : currentNodeInfo.right - nextNodeInfo.left;
  368. let barCalcedWidth = barWidth + spaceOffset * Math.abs(dxRate);
  369. if (isRight) {
  370. if (barCalcedWidth > nextBottomX - this.bottomDotX + barWidth) {
  371. const barMinusWidth = barWidth + spaceOffset * (1 - dxRate);
  372. this.bottomDotX = this.bottomDotXForIndex + (barCalcedWidth - barMinusWidth) / 2;
  373. barCalcedWidth = barMinusWidth;
  374. }
  375. }else if (!isRight) {
  376. if (barCalcedWidth > this.bottomDotXForIndex + barWidth - nextBottomX){
  377. const barMinusWidth = barWidth + spaceOffset * (1 + dxRate);
  378. barCalcedWidth = barMinusWidth;
  379. this.bottomDotX = nextBottomX;
  380. } else{
  381. this.bottomDotX = this.bottomDotXForIndex - (barCalcedWidth - barWidth);
  382. }
  383. }
  384. barCalcedWidth = Math.max(barCalcedWidth, barWidth);
  385. this.barCalcedWidth = barCalcedWidth;
  386. }
  387. },
  388. //在swiper的@animationfinish中通知z-tabs结束多setDx的锁定,若在父组件中调用了setDx,则必须调用unlockDx
  389. unlockDx() {
  390. this.shouldSetDx = true;
  391. },
  392. //更新z-tabs内部布局
  393. updateSubviewLayout(tryCount = 0) {
  394. this.$nextTick(() => {
  395. let delayTime = 10;
  396. // #ifdef APP-NVUE || MP-BAIDU
  397. delayTime = 50;
  398. // #endif
  399. setTimeout(() => {
  400. this._getNodeClientRect('.z-tabs-scroll-view-conatiner').then(res=>{
  401. if (res){
  402. if (!res[0].width && tryCount < 10) {
  403. setTimeout(() => {
  404. tryCount ++;
  405. this.updateSubviewLayout(tryCount);
  406. }, 50);
  407. return;
  408. }
  409. this.tabsWidth = res[0].width;
  410. this.tabsHeight = res[0].height;
  411. this.tabsLeft = res[0].left;
  412. this._handleListChange(this.list);
  413. }
  414. })
  415. this._getNodeClientRect('.z-tabs-conatiner').then(res=>{
  416. if(res && res[0].width){
  417. this.tabsSuperWidth = res[0].width;
  418. }
  419. })
  420. },delayTime)
  421. })
  422. },
  423. //点击了tabs
  424. tabsClick(index,item) {
  425. if (item.disabled) return;
  426. if (this.currentIndex != index) {
  427. this.shouldSetDx = false;
  428. this.$emit('change', index, item[this.valueKey]);
  429. this.currentIndex = index;
  430. this._preUpdateDotPosition(index);
  431. }
  432. },
  433. //scroll-view滚动
  434. scroll(e){
  435. this.currentScrollLeft = e.detail.scrollLeft;
  436. },
  437. //更新底部dot位置之前的预处理
  438. _preUpdateDotPosition(index) {
  439. // #ifndef APP-NVUE
  440. this.$nextTick(() => {
  441. uni.createSelectorQuery().in(this).select(".z-tabs-scroll-view").fields({
  442. scrollOffset: true
  443. }, data => {
  444. if (data) {
  445. this.currentScrollLeft = data.scrollLeft;
  446. this._updateDotPosition(index);
  447. } else {
  448. this._updateDotPosition(index);
  449. }
  450. }).exec();
  451. })
  452. // #endif
  453. // #ifdef APP-NVUE
  454. this._updateDotPosition(index);
  455. // #endif
  456. },
  457. //更新底部dot位置
  458. _updateDotPosition(index){
  459. if(index >= this.itemNodeInfos.length) return;
  460. this.$nextTick(async ()=>{
  461. let node = this.itemNodeInfos[index];
  462. let offset = 0;
  463. let tabsContainerWidth = this.tabsContainerWidth;
  464. if (JSON.stringify(this.activeStyle) !== '{}') {
  465. const nodeRes = await this._getNodeClientRect(`#z-tabs-item-${index}`,true);
  466. if (nodeRes) {
  467. node = nodeRes[0];
  468. offset = this.currentScrollLeft;
  469. this.tabsHeight = Math.max(node.height + uni.upx2px(28), this.tabsHeight);
  470. tabsContainerWidth = 0;
  471. for(let i = 0;i < this.itemNodeInfos.length;i++){
  472. let oldNode = this.itemNodeInfos[i];
  473. tabsContainerWidth += i === index ? node.width : oldNode.width;
  474. }
  475. }
  476. }
  477. this.bottomDotX = this._getBottomDotX(node, this.finalBarWidth, offset);
  478. this.bottomDotXForIndex = this.bottomDotX;
  479. if (this.tabsWidth) {
  480. setTimeout(()=>{
  481. let scrollLeft = this.bottomDotX - this.tabsWidth / 2 + this.finalBarWidth / 2;
  482. scrollLeft = Math.max(0,scrollLeft);
  483. if (tabsContainerWidth) {
  484. scrollLeft = Math.min(scrollLeft,tabsContainerWidth - this.tabsWidth + 10);
  485. }
  486. if (this.shouldScroll && tabsContainerWidth > this.tabsWidth) {
  487. this.scrollLeft = scrollLeft;
  488. }
  489. this.$nextTick(()=>{
  490. this.isFirstLoaded = true;
  491. })
  492. },200)
  493. }
  494. })
  495. },
  496. // 处理list改变
  497. _handleListChange(newVal) {
  498. this.$nextTick(async ()=>{
  499. if(newVal.length){
  500. let itemNodeInfos = [];
  501. let tabsContainerWidth = 0;
  502. let delayTime = 0;
  503. // #ifdef MP-BAIDU
  504. delayTime = 100;
  505. // #endif
  506. setTimeout(async()=>{
  507. for(let i = 0;i < newVal.length;i++){
  508. const nodeRes = await this._getNodeClientRect(`#z-tabs-item-${i}`,true);
  509. if(nodeRes){
  510. const node = nodeRes[0];
  511. node.left += this.currentScrollLeft;
  512. itemNodeInfos.push(node);
  513. tabsContainerWidth += node.width;
  514. }
  515. if (i === this.currentIndex){
  516. this.itemNodeInfos = itemNodeInfos;
  517. this.tabsContainerWidth = tabsContainerWidth;
  518. this._updateDotPosition(this.currentIndex);
  519. }
  520. }
  521. this.itemNodeInfos = itemNodeInfos;
  522. this.tabsContainerWidth = tabsContainerWidth;
  523. this._updateDotPosition(this.currentIndex);
  524. },delayTime)
  525. }
  526. })
  527. if (this.initTriggerChange && this.changeTriggerFailed && newVal.length) {
  528. if (this.current < newVal.length) {
  529. this.$emit('change', this.current, newVal[this.current][this.valueKey]);
  530. }
  531. }
  532. },
  533. //根据node获取bottomX
  534. _getBottomDotX(node, barWidth = this.finalBarWidth, offset = 0){
  535. return node.left + node.width / 2 - barWidth / 2 + offset - this.tabsLeft;
  536. },
  537. //获取节点信息
  538. _getNodeClientRect(select, withRefArr = false) {
  539. // #ifdef APP-NVUE
  540. select = select.replace('.', '').replace('#', '');
  541. const ref = withRefArr ? this.$refs[select][0] : this.$refs[select];
  542. return new Promise((resolve, reject) => {
  543. if (ref) {
  544. weexDom.getComponentRect(ref, option => {
  545. if (option && option.result) {
  546. resolve([option.size]);
  547. } else resolve(false);
  548. })
  549. } else resolve(false);
  550. });
  551. return;
  552. // #endif
  553. const res = uni.createSelectorQuery().in(this);
  554. res.select(select).boundingClientRect();
  555. return new Promise((resolve, reject) => {
  556. res.exec(data => {
  557. resolve((data && data != '' && data != undefined && data.length) ? data : false);
  558. });
  559. });
  560. },
  561. //格式化badge中的count
  562. _formatCount(count) {
  563. if (!count) return '';
  564. if (count > this.badgeMaxCount) {
  565. return this.badgeMaxCount + '+';
  566. }
  567. return count.toString();
  568. },
  569. //将文本的px或者rpx转为px的值
  570. _convertTextToPx(text) {
  571. const dataType = Object.prototype.toString.call(text);
  572. if (dataType === '[object Number]') {
  573. return uni.upx2px(text);
  574. }
  575. let isRpx = false;
  576. if (text.indexOf('rpx') !== -1 || text.indexOf('upx') !== -1) {
  577. text = text.replace('rpx', '').replace('upx', '');
  578. isRpx = true;
  579. } else if (text.indexOf('px') !== -1) {
  580. text = text.replace('px', '');
  581. } else {
  582. text = uni.upx2px(text);
  583. }
  584. if (!isNaN(text)) {
  585. if (isRpx) return Number(uni.upx2px(text));
  586. return Number(text);
  587. }
  588. return 0;
  589. }
  590. }
  591. }
  592. </script>
  593. <style scoped>
  594. .z-tabs-conatiner{
  595. /* #ifndef APP-NVUE */
  596. overflow: hidden;
  597. display: flex;
  598. width: 100%;
  599. /* #endif */
  600. /* #ifdef APP-NVUE */
  601. width: 750rpx;
  602. /* #endif */
  603. flex-direction: row;
  604. height: 103rpx;
  605. }
  606. .z-tabs-scroll-view-conatiner{
  607. flex: 1;
  608. position: relative;
  609. /* #ifndef APP-NVUE */
  610. display: flex;
  611. height: 100%;
  612. width: 100%;
  613. /* #endif */
  614. flex-direction: row;
  615. }
  616. /* #ifndef APP-NVUE */
  617. .z-tabs-scroll-view ::-webkit-scrollbar {
  618. display: none;
  619. -webkit-appearance: none;
  620. width: 0 !important;
  621. height: 0 !important;
  622. background: transparent;
  623. }
  624. /* #endif */
  625. .z-tabs-scroll-view{
  626. flex-direction: row;
  627. position: absolute;
  628. left: 0;
  629. top: 0;
  630. right: 0;
  631. bottom: 0;
  632. /* #ifndef APP-NVUE */
  633. width: 100%;
  634. height: 100%;
  635. /* #endif */
  636. flex: 1;
  637. }
  638. .z-tabs-list-container{
  639. position: relative;
  640. /* #ifndef APP-NVUE */
  641. height: 100%;
  642. /* #endif */
  643. }
  644. .z-tabs-list,.z-tabs-list-container{
  645. /* #ifndef APP-NVUE */
  646. display: flex;
  647. /* #endif */
  648. flex-direction: row;
  649. }
  650. .z-tabs-item{
  651. /* #ifndef APP-NVUE */
  652. display: flex;
  653. /* #endif */
  654. flex-direction: row;
  655. justify-content: center;
  656. align-items: center;
  657. padding: 0px 20rpx;
  658. }
  659. .z-tabs-item-title-container{
  660. /* #ifndef APP-NVUE */
  661. display: flex;
  662. /* #endif */
  663. flex-direction: row;
  664. align-items: center;
  665. }
  666. .z-tabs-item-title{
  667. font-size: 30rpx;
  668. }
  669. .z-tabs-item-title-disabled{
  670. /* #ifndef APP-NVUE */
  671. cursor: not-allowed;
  672. /* #endif */
  673. }
  674. .z-tabs-item-badge{
  675. margin-left: 8rpx;
  676. background-color: #ec5b56;
  677. color: white;
  678. font-size: 22rpx;
  679. border-radius: 100px;
  680. padding: 0rpx 10rpx;
  681. }
  682. .z-tabs-bottom{
  683. position: absolute;
  684. bottom: 0;
  685. left: 0;
  686. right: 0;
  687. }
  688. .z-tabs-bottom-dot{
  689. border-radius: 100px;
  690. }
  691. .z-tabs-left,.z-tabs-right{
  692. /* #ifndef APP-NVUE */
  693. display: flex;
  694. /* #endif */
  695. flex-direction: row;
  696. align-items: center;
  697. }
  698. </style>