cl-footer.uvue 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. <template>
  2. <view
  3. class="cl-footer-placeholder"
  4. :style="{ height: getUnit(placeholderHeight) }"
  5. v-if="visible"
  6. >
  7. </view>
  8. <view class="cl-footer-wrapper" :class="[pt.wrapper?.className]">
  9. <view
  10. class="cl-footer"
  11. :class="[
  12. {
  13. 'is-dark': isDark
  14. },
  15. pt.className
  16. ]"
  17. :style="{ backgroundColor }"
  18. v-if="visible"
  19. >
  20. <view class="cl-footer__content" :class="[pt.content?.className]" :style="contentStyle">
  21. <slot></slot>
  22. </view>
  23. </view>
  24. </view>
  25. </template>
  26. <script setup lang="ts">
  27. import { getConfig, getSafeAreaHeight, getUnit, isDark, isHarmony, parsePt } from "@/.cool";
  28. import { computed, getCurrentInstance, nextTick, onMounted, ref, watch } from "vue";
  29. import type { PassThroughProps } from "../../types";
  30. import { clFooterOffset } from "./offset";
  31. defineOptions({
  32. name: "cl-footer"
  33. });
  34. const props = defineProps({
  35. pt: {
  36. type: Object,
  37. default: () => ({})
  38. },
  39. // 最小高度,小于该高度时,不显示
  40. minHeight: {
  41. type: Number,
  42. default: 30
  43. },
  44. // 监听值,触发更新
  45. vt: {
  46. type: Number,
  47. default: 0
  48. },
  49. // 内容高度
  50. height: {
  51. type: Number,
  52. default: null
  53. },
  54. // 背景颜色
  55. backgroundColor: {
  56. type: String,
  57. default: null
  58. }
  59. });
  60. const { proxy } = getCurrentInstance()!;
  61. type PassThrough = {
  62. className?: string;
  63. content?: PassThroughProps;
  64. wrapper?: PassThroughProps;
  65. };
  66. const pt = computed(() => parsePt<PassThrough>(props.pt));
  67. // 内容样式
  68. const contentStyle = computed(() => {
  69. const style = {};
  70. if (props.height != null) {
  71. style["height"] = props.height + "px";
  72. }
  73. return style;
  74. });
  75. // 背景色
  76. const backgroundColor = computed(() => {
  77. if (props.backgroundColor == null) {
  78. return isDark.value ? getConfig("tabBgColor") : "white";
  79. } else {
  80. return props.backgroundColor;
  81. }
  82. });
  83. // 占位高度
  84. const placeholderHeight = ref(0);
  85. // 是否显示
  86. const visible = ref(true);
  87. // 设置内容高度
  88. function setHeight(val: number) {
  89. // 设置高度
  90. placeholderHeight.value = val;
  91. // 如果内容高度大于最小高度,则显示
  92. visible.value = val > props.minHeight + getSafeAreaHeight("bottom");
  93. // 隔离高度
  94. clFooterOffset.set(visible.value ? val : 0);
  95. }
  96. // 获取内容高度
  97. function getHeight() {
  98. if (props.height != null) {
  99. setHeight(props.height + getSafeAreaHeight("bottom"));
  100. return;
  101. }
  102. nextTick(() => {
  103. setTimeout(
  104. () => {
  105. uni.createSelectorQuery()
  106. .in(proxy)
  107. .select(".cl-footer")
  108. .boundingClientRect((res) => {
  109. setHeight(Math.floor((res as NodeInfo).height ?? 0));
  110. })
  111. .exec();
  112. },
  113. isHarmony() ? 50 : 0
  114. );
  115. });
  116. }
  117. onMounted(() => {
  118. watch(
  119. computed(() => props.vt),
  120. () => {
  121. visible.value = true;
  122. getHeight();
  123. },
  124. {
  125. immediate: true
  126. }
  127. );
  128. });
  129. </script>
  130. <style lang="scss" scoped>
  131. .cl-footer {
  132. @apply overflow-visible;
  133. padding-bottom: env(safe-area-inset-bottom);
  134. &__content {
  135. @apply px-3 py-3 overflow-visible;
  136. }
  137. &-wrapper {
  138. @apply fixed bottom-0 left-0 w-full overflow-visible;
  139. }
  140. }
  141. </style>