<script setup>
import Empty from "../Empty/Empty.vue";
import { ref, onMounted, watchEffect } from "vue";
import { router, getRoute, normalizeParams } from "../../utils/router";
import { getRect, debounce } from "../../utils";
import { getCurrentInstance } from "vue";
import pages from "@/pages.json";

const props = defineProps({
  showBottom: {
    type: Boolean,
    default: false,
  },
  bottomText: {
    type: String,
    default: "~已经到底了~",
  },
  scrollX: {
    type: Boolean,
    default: false,
  },
  scrollY: {
    type: Boolean,
    default: true,
  },
  className: {
    type: String,
    default: "",
  },
  scrollIntoView: {
    type: String,
    default: "",
  },
  scrollStyle: {
    type: Object,
    default: () => ({}),
  },
  onBack: Function,
  showBack: {
    type: Boolean,
    default: true,
  },
  showTitle: {
    type: Boolean,
    default: true,
  },
  bgColor: {
    type: String,
    default: "#fff",
  },
  title: String,
  empty: {
    type: [Boolean],
    default: false,
  },
  border: {
    type: Boolean,
    default: false,
  },
  scrollTop: {
    type: Number,
    default: 0,
  },
  headerColor: {
    type: String,
    default: "transparent",
  },
  bottomBgColor: {
    type: String,
    default: "transparent",
  },
  bottomBorder: {
    type: Boolean,
    default: false,
  },
  themeColor: {
    type: String,
    default: "#333",
  },
});

const emit = defineEmits(["onSafeAreaChange", "onScroll", "scrolltolower"]);

const onBack = async () => {
  if (props.onBack) {
    const res = await props.onBack();
    if (!res) return;
    if (typeof res === "string") {
      router.reLaunch(normalizeParams(res));
      return;
    }
    router.back();
    return;
  }
  router.back();
};

const safeArea = ref({
  footer: {},
  title: {},
});
const footerHeight = ref(0);

const menuButtonInfo = ref({
  top: 0,
  height: 0,
  width: 0,
});

onMounted(() => {
  const _menuButtonInfo = uni.getMenuButtonBoundingClientRect();

  menuButtonInfo.value = {
    top: _menuButtonInfo.top,
    height: _menuButtonInfo.height,
    width: _menuButtonInfo.width,
  };

  const systemInfo = uni.getWindowInfo();
  // 判断是否为tabbar页面
  safeArea.value = {
    ...systemInfo.safeArea,
    source: systemInfo.safeArea,
  };

  // 解决部分安卓手机安全顶部为0 的bug
  if (!safeArea.value.top) {
    safeArea.value.top = systemInfo.statusBarHeight;
  }

  const isTarbarPage = pages.tabBar.list
    .map((item) => item.pagePath)
    .includes(getRoute().routeList[0].path);

  if (
    safeArea.value.source.bottom === safeArea.value.source.height &&
    isTarbarPage
  ) {
    safeArea.value.height -= 50;
  }

  if (props.scrollStyle?.padding === void 0) {
    // 24是内边距
    safeArea.value.width -= 24;
  }

  const instance = getCurrentInstance();
  // 获取头部高度
  getRect({
    name: ".title",
    onSuccess(res) {
      safeArea.value.title = res;
    },
    instance,
  });
  // 获取底部高度
  getRect({
    name: ".bottom-button",
    onSuccess(res) {
      safeArea.value.footer = res;
    },
    instance,
  });
});

watchEffect(() => {
  emit("onSafeAreaChange", safeArea.value);
});

const onScrolltolower = (e) => {
  emit('scrolltolower', e)
};

const onScroll = debounce((e) => {
  emit("onScroll", e);
}, 100);

defineExpose({
  safeArea: safeArea.value,
});
</script>

<template>
  <view class="container" :style="{ background: bgColor }">
    <view class="bg" v-if="$slots.bg">
      <slot name="bg"></slot>
    </view>
    <view
      class="title"
      :style="{
        paddingTop: `${safeArea.top}px`,
        background: headerColor,
      }"
      v-if="showTitle"
    >
      <uni-nav-bar
        :left-icon="showBack ? 'left' : ''"
        @clickLeft="onBack"
        :border="border"
        :title="title"
        :color="themeColor"
        fixed
        :backgroundColor="headerColor"
      />
    </view>
    <scroll-view
      enable-flex
      :show-scrollbar="false"
      enable-back-to-top
      scroll-with-animation
      scroll-anchoring
      :scroll-x="scrollX"
      :scroll-y="scrollY"
      :scroll-top="scrollTop"
      @scroll="onScroll"
      @scrolltolower="onScrolltolower"
      :class="['scroll-view', scrollX ? 'topic-scroll' : '']"
      :style="{
        height: `calc(${
          safeArea.height - safeArea.top - safeArea.title?.height - 100
        }px - env(safe-area-inset-bottom))`,
        width: safeArea.width + 'px',
        whiteSpace: scrollX ? 'nowrap' : 'normal',
        flex: 1,
        ...scrollStyle,
      }"
      v-if="!empty"
    >
      <slot></slot>
      <!-- 底部文字 -->
      <view v-if="showBottom && bottomText" class="bottom-text">
        {{ bottomText }}
      </view>
    </scroll-view>
    <view class="empty" v-else>
      <Empty v-bind="$attrs" />
    </view>
    <slot name="stick"></slot>
    <view
      v-if="$slots.footer"
      class="bottom-button"
      :style="{
        background: bottomBgColor,
        borderTop: bottomBorder ? '1px solid #eee' : 'none',
      }"
    >
      <slot name="footer" :footer-height="footerHeight" />
    </view>
  </view>
</template>

<style lang="scss" scoped>
@import "@/uni.scss";
scroll-view ::-webkit-scrollbar {
  width: 0;
  height: 0;
  background-color: transparent;
}
.title {
  position: sticky;
  top: 0;
  z-index: 9999;
}

.bg {
  position: absolute;
  top: 0;
}

.empty {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 1;
}

.container {
  display: flex;
  flex-direction: column;
  color: #333;
  box-sizing: content-box;
  position: relative;
  overflow: hidden;
  height: 100vh;
}

.scroll-view {
  position: relative;
  padding: 24rpx;
  box-sizing: content-box;
}

.bottom-text {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
}

.bottom-button {
  width: 100%;
  padding: 24rpx;
  position: absolute;
  bottom: 0;
  box-sizing: border-box;
}

.topic-scroll {
  white-space: nowrap;
  display: inline-flex;
}
</style>
