图书馆小程序
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

197 lines
4.3 KiB

2 weeks ago
1 week ago
2 weeks ago
  1. <template>
  2. <view class="tab-container">
  3. <view class="tab-box">
  4. <scroll-view
  5. id="_scroll"
  6. scroll-x
  7. class="scroll-view"
  8. scroll-with-animation
  9. :scroll-left="scrollLeft"
  10. >
  11. <view class="scroll-content">
  12. <view class="tab-item-box">
  13. <block v-for="(item, index) in tabList" :key="index">
  14. <view
  15. class="tab-item"
  16. :id="'_tab_' + index"
  17. :class="{ 'tab-item-active': activeIndex === index }"
  18. @click="tabClick(index)"
  19. :style="{
  20. color: activeIndex === index ? defaultConfig.activeTextColor : defaultConfig.textColor
  21. }"
  22. >{{ item.label || item }}</view>
  23. </block>
  24. </view>
  25. <!-- 滑块 -->
  26. <view
  27. class="underLine"
  28. :style="{
  29. transform: 'translateX(' + slider.left + 'px)',
  30. width: defaultConfig.underLineWidth + 'px',
  31. height: defaultConfig.underLineHeight + 'px',
  32. backgroundColor: defaultConfig.underLineColor
  33. }"
  34. />
  35. </view>
  36. </scroll-view>
  37. </view>
  38. </view>
  39. </template>
  40. <script>
  41. export default {
  42. name: 'my-tabs',
  43. props: {
  44. tabData: {
  45. type: Array,
  46. default: () => []
  47. },
  48. defaultIndex: {
  49. type: Number,
  50. default: 0
  51. },
  52. config: {
  53. type: Object,
  54. default: () => ({})
  55. }
  56. },
  57. data() {
  58. return {
  59. tabList: [],
  60. activeIndex: 0,
  61. slider: { left: 0 },
  62. scrollLeft: 0,
  63. defaultConfig: {
  64. textColor: '#333333',
  65. activeTextColor: '#01a4fe',
  66. underLineWidth: 24,
  67. underLineHeight: 2,
  68. underLineColor: '#01a4fe'
  69. }
  70. }
  71. },
  72. watch: {
  73. tabData: {
  74. handler(val) {
  75. this.tabList = val
  76. this.$nextTick(() => {
  77. this.updateTabWidth()
  78. })
  79. },
  80. immediate: true
  81. },
  82. defaultIndex: {
  83. handler(val) {
  84. if (this.tabList.length === 0) return
  85. this.activeIndex = val
  86. this.$nextTick(() => {
  87. this.updateTabWidth()
  88. })
  89. },
  90. immediate: true
  91. },
  92. config: {
  93. handler(val) {
  94. this.defaultConfig = { ...this.defaultConfig, ...val }
  95. },
  96. immediate: true
  97. }
  98. },
  99. methods: {
  100. updateTabWidth() {
  101. if (this.tabList.length === 0) return
  102. const query = uni.createSelectorQuery().in(this)
  103. let finishCount = 0
  104. this.tabList.forEach((item, index) => {
  105. query.select(`#_tab_${index}`).boundingClientRect((res) => {
  106. if (!res) return
  107. item._slider = {
  108. left: res.left + (res.width - this.defaultConfig.underLineWidth) / 2
  109. }
  110. finishCount++
  111. if (finishCount === this.tabList.length) {
  112. this.tabToIndex()
  113. }
  114. }).exec()
  115. })
  116. },
  117. tabClick(index) {
  118. this.activeIndex = index
  119. this.tabToIndex()
  120. this.$emit('tabClick', index)
  121. },
  122. tabToIndex() {
  123. if (!this.tabList.length || !this.tabList[this.activeIndex]?._slider) {
  124. return
  125. }
  126. this.slider.left = this.tabList[this.activeIndex]._slider.left
  127. }
  128. }
  129. }
  130. </script>
  131. <style lang="scss" scoped>
  132. .tab-container {
  133. font-size: 14px;
  134. height: 45px;
  135. line-height: 45px;
  136. .tab-box {
  137. width: 100%;
  138. height: 45px;
  139. display: flex;
  140. position: relative;
  141. .scroll-view {
  142. white-space: nowrap;
  143. width: 100%;
  144. height: 100%;
  145. box-sizing: border-box;
  146. .scroll-content {
  147. width: 100%;
  148. height: 100%;
  149. position: relative;
  150. .tab-item-box {
  151. display: flex;
  152. justify-content: space-around;
  153. height: 100%;
  154. .tab-item {
  155. height: 100%;
  156. display: inline-block;
  157. text-align: center;
  158. padding: 0 20px;
  159. position: relative;
  160. color: #333;
  161. &-active {
  162. color: #01a4fe;
  163. }
  164. }
  165. }
  166. .underLine {
  167. height: 2px;
  168. width: 25px;
  169. background-color: #01a4fe;
  170. border-radius: 3px;
  171. transition: 0.3s;
  172. position: absolute;
  173. bottom: 0;
  174. }
  175. }
  176. }
  177. }
  178. /* #ifdef H5 */
  179. ::v-deep .uni-scroll-view::-webkit-scrollbar {
  180. display: none;
  181. }
  182. /* #endif */
  183. }
  184. </style>