tabs.vue 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. <template>
  2. <scroll-view
  3. scroll-x="true"
  4. scroll-with-animation
  5. :scroll-left="scrollLeft"
  6. ref="tabsscroll"
  7. class="colpu__tabs-header"
  8. :style="{
  9. backgroundColor: backgroundColor,
  10. }"
  11. >
  12. <view class="colpu__tabs-header-scroll" :style="styles">
  13. <template v-for="(item, idx) in dataList" :key="idx">
  14. <view
  15. class="colpu__tabs-header-item"
  16. :class="{ active: selectIndex === idx }"
  17. @tap="selectHanddle(idx)"
  18. >
  19. {{ item[keyName] }}
  20. </view>
  21. </template>
  22. </view>
  23. <view
  24. v-if="showHover"
  25. class="colpu__tabs-header-hover"
  26. :style="{
  27. left: hoverDetail.left + 'px',
  28. width: hoverDetail.width + 'px',
  29. borderRadius: borderRadius,
  30. bottom: bottom + 'px',
  31. }"
  32. ></view>
  33. </scroll-view>
  34. </template>
  35. <script setup>
  36. /**
  37. * tab最大支持10个,首scroll-view限制,会导致计算错误
  38. */
  39. import { getCurrentInstance, onMounted, ref, watch } from "vue";
  40. const scrollLeft = ref(0);
  41. const hoverDetail = ref({ left: 0, width: 0 });
  42. const tabListDetail = ref([]);
  43. const selectIndex = ref(0);
  44. const tabsDetail = ref({ left: 0, width: 0 });
  45. const props = defineProps({
  46. dataList: {
  47. type: Array,
  48. default: () => [],
  49. },
  50. index: { type: Number, default: 0 },
  51. bottom: {
  52. type: Number,
  53. default: 0,
  54. },
  55. styles: {
  56. type: [Object, String],
  57. default: () => ({}),
  58. },
  59. borderHeight: {
  60. type: Number,
  61. default: 2,
  62. },
  63. borderRadius: {
  64. type: String,
  65. default: "3px",
  66. },
  67. backgroundColor: {
  68. type: String,
  69. default: "none",
  70. },
  71. keyName: {
  72. type: String,
  73. default: "name",
  74. },
  75. showHover: {
  76. type: Boolean,
  77. default: true,
  78. },
  79. });
  80. const emits = defineEmits({
  81. select: null,
  82. });
  83. const getSelectQuery = () => {
  84. const query = uni.createSelectorQuery().in(getCurrentInstance());
  85. const pr = [];
  86. pr.push(
  87. new Promise((res) => {
  88. query
  89. .selectAll(".colpu__tabs-header-item")
  90. .boundingClientRect((data) => {
  91. tabListDetail.value = data;
  92. res(data);
  93. })
  94. .exec();
  95. })
  96. );
  97. pr.push(
  98. new Promise((res) => {
  99. query
  100. .selectAll(".colpu__tabs-header")
  101. .boundingClientRect((value) => {
  102. tabsDetail.value = value[0];
  103. res(value[0]);
  104. })
  105. .exec();
  106. })
  107. );
  108. return Promise.all(pr);
  109. };
  110. onMounted(async () => {
  111. await getSelectQuery();
  112. selectHanddle(props.index, false);
  113. });
  114. const selectHanddle = (index, isEmit = true) => {
  115. const halfWidth = tabsDetail.value.width / 2;
  116. selectIndex.value = index;
  117. const data = tabListDetail.value[index] || {};
  118. let { left = 0, width = 0 } = data;
  119. if (halfWidth > left) {
  120. scrollLeft.value = 0;
  121. } else {
  122. scrollLeft.value = left - halfWidth + width / 2;
  123. }
  124. hoverDetail.value.left = left;
  125. hoverDetail.value.width = width - 10;
  126. isEmit && emits("select", { index, data: props.dataList[index] });
  127. };
  128. defineExpose({
  129. switchTab: (index) => {
  130. selectHanddle(index);
  131. },
  132. });
  133. watch(
  134. () => props.dataList,
  135. (value, ov) => {
  136. selectIndex.value = props.index;
  137. if (props.index !== 0) {
  138. selectHanddle(props.index, false);
  139. }
  140. }
  141. );
  142. </script>
  143. <style lang="scss">
  144. $color-primary: #001d4f;
  145. $color-tips: #999;
  146. .colpu__tabs-header {
  147. position: relative;
  148. flex: 1;
  149. $h: 48rpx;
  150. &-scroll {
  151. display: flex;
  152. align-items: center;
  153. flex-wrap: nowrap;
  154. align-content: stretch;
  155. justify-content: space-around;
  156. height: $h;
  157. .active {
  158. color: $color-primary;
  159. font-weight: 800;
  160. }
  161. }
  162. &-hover {
  163. $hoverH: 4rpx;
  164. position: absolute;
  165. width: 0;
  166. height: $hoverH;
  167. top: $h - $hoverH;
  168. border-radius: $hoverH;
  169. background-color: $color-primary;
  170. bottom: 0;
  171. transition: all 0.3s ease;
  172. }
  173. &-item {
  174. position: relative;
  175. margin: 0 10px;
  176. flex-shrink: 0;
  177. display: flex;
  178. justify-content: space-between;
  179. font-size: 28rpx;
  180. color: $color-tips;
  181. }
  182. }
  183. </style>