From a1d7e81859f554f3a53680cc35f0f49bf1f77098 Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期四, 14 五月 2026 14:37:02 +0800
Subject: [PATCH] 导入项目

---
 src/components/Sticky/src/Sticky.vue |  143 +++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 143 insertions(+), 0 deletions(-)

diff --git a/src/components/Sticky/src/Sticky.vue b/src/components/Sticky/src/Sticky.vue
new file mode 100644
index 0000000..28ecbcb
--- /dev/null
+++ b/src/components/Sticky/src/Sticky.vue
@@ -0,0 +1,143 @@
+<script lang="ts" setup>
+import { propTypes } from '@/utils/propTypes'
+import { isClient, useEventListener, useWindowSize } from '@vueuse/core'
+import type { CSSProperties } from 'vue'
+
+defineOptions({ name: 'Sticky' })
+
+const props = defineProps({
+  // 璺濈椤堕儴鎴栬�呭簳閮ㄧ殑璺濈(鍗曚綅px)
+  offset: propTypes.number.def(0),
+  // 璁剧疆鍏冪礌鐨勫爢鍙犻『搴�
+  zIndex: propTypes.number.def(999),
+  // 璁剧疆鎸囧畾鐨刢lass
+  className: propTypes.string.def(''),
+  // 瀹氫綅鏂瑰紡锛岄粯璁や负(top)锛岃〃绀鸿窛绂婚《閮ㄤ綅缃紝鍙互璁剧疆涓簍op鎴栬�卋ottom
+  position: {
+    type: String,
+    validator: function (value: string) {
+      return ['top', 'bottom'].indexOf(value) !== -1
+    },
+    default: 'top'
+  }
+})
+const width = ref('auto' as string)
+const height = ref('auto' as string)
+const isSticky = ref(false)
+const refSticky = shallowRef<HTMLElement>()
+const scrollContainer = shallowRef<HTMLElement | Window>()
+const { height: windowHeight } = useWindowSize()
+onMounted(() => {
+  height.value = refSticky.value?.getBoundingClientRect().height + 'px'
+
+  scrollContainer.value = getScrollContainer(refSticky.value!, true)
+  useEventListener(scrollContainer, 'scroll', handleScroll)
+  useEventListener('resize', handleResize)
+  handleScroll()
+})
+onActivated(() => {
+  handleScroll()
+})
+
+const camelize = (str: string): string => {
+  return str.replace(/-(\w)/g, (_, c) => (c ? c.toUpperCase() : ''))
+}
+
+const getStyle = (element: HTMLElement, styleName: keyof CSSProperties): string => {
+  if (!isClient || !element || !styleName) return ''
+
+  let key = camelize(styleName)
+  if (key === 'float') key = 'cssFloat'
+  try {
+    const style = element.style[styleName]
+    if (style) return style
+    const computed = document.defaultView?.getComputedStyle(element, '')
+    return computed ? computed[styleName] : ''
+  } catch {
+    return element.style[styleName]
+  }
+}
+const isScroll = (el: HTMLElement, isVertical?: boolean): boolean => {
+  if (!isClient) return false
+  const key = (
+    {
+      undefined: 'overflow',
+      true: 'overflow-y',
+      false: 'overflow-x'
+    } as const
+  )[String(isVertical)]!
+  const overflow = getStyle(el, key)
+  return ['scroll', 'auto', 'overlay'].some((s) => overflow.includes(s))
+}
+
+const getScrollContainer = (
+  el: HTMLElement,
+  isVertical: boolean
+): Window | HTMLElement | undefined => {
+  if (!isClient) return
+  let parent = el
+  while (parent) {
+    if ([window, document, document.documentElement].includes(parent)) return window
+    if (isScroll(parent, isVertical)) return parent
+    parent = parent.parentNode as HTMLElement
+  }
+  return parent
+}
+
+const handleScroll = () => {
+  width.value = refSticky.value!.getBoundingClientRect().width! + 'px'
+  if (props.position === 'top') {
+    const offsetTop = refSticky.value?.getBoundingClientRect().top
+    if (offsetTop !== undefined && offsetTop < props.offset) {
+      sticky()
+      return
+    }
+    reset()
+  } else {
+    const offsetBottom = refSticky.value?.getBoundingClientRect().bottom
+
+    if (offsetBottom !== undefined && offsetBottom > windowHeight.value - props.offset) {
+      sticky()
+      return
+    }
+    reset()
+  }
+}
+const handleResize = () => {
+  if (isSticky.value && refSticky.value) {
+    width.value = refSticky.value.getBoundingClientRect().width + 'px'
+  }
+}
+const sticky = () => {
+  if (isSticky.value) {
+    return
+  }
+  isSticky.value = true
+}
+const reset = () => {
+  if (!isSticky.value) {
+    return
+  }
+  width.value = 'auto'
+  isSticky.value = false
+}
+</script>
+<template>
+  <div ref="refSticky" :style="{ height: height, zIndex: zIndex }">
+    <div
+      :class="className"
+      :style="{
+        top: position === 'top' ? offset + 'px' : '',
+        bottom: position !== 'top' ? offset + 'px' : '',
+        zIndex: zIndex,
+        position: isSticky ? 'fixed' : 'static',
+        width: width,
+        height: height
+      }"
+    >
+      <slot>
+        <div>sticky</div>
+      </slot>
+    </div>
+  </div>
+</template>

--
Gitblit v1.8.0