149 lines
4.7 KiB
Vue
149 lines
4.7 KiB
Vue
|
|
<!-- @/components/StickyDragContainer/index.vue -->
|
|||
|
|
<template>
|
|||
|
|
<div class="sticky-drag-wrapper" :style="wrapperStyle">
|
|||
|
|
<!-- 拖拽条 -->
|
|||
|
|
<div class="drag-bar" ref="dragBarRef" @mousedown="handleDragStart"></div>
|
|||
|
|
<!-- 高度可调节+内部滚动容器 -->
|
|||
|
|
<div
|
|||
|
|
class="content-scroll-container"
|
|||
|
|
ref="scrollContainerRef"
|
|||
|
|
:style="{ height: `${containerHeight}px` }"
|
|||
|
|
>
|
|||
|
|
<!-- 默认插槽:接收外部传入的任意内容 -->
|
|||
|
|
<slot></slot>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup name="StickyDragContainer">
|
|||
|
|
import { ref, onMounted, onUnmounted, defineProps, watch, nextTick } from 'vue'
|
|||
|
|
|
|||
|
|
// 新增:接收父容器Ref(必传,用于获取父容器布局信息)
|
|||
|
|
const props = defineProps({
|
|||
|
|
parentRef: { // 父容器的Ref对象,外部传入
|
|||
|
|
type: Object,
|
|||
|
|
required: true
|
|||
|
|
},
|
|||
|
|
initialHeight: { // 容器初始高度(px)
|
|||
|
|
type: Number,
|
|||
|
|
default: 300
|
|||
|
|
},
|
|||
|
|
minHeight: { // 容器最小高度(px)
|
|||
|
|
type: Number,
|
|||
|
|
default: 200
|
|||
|
|
},
|
|||
|
|
zIndex: { // 容器层级
|
|||
|
|
type: Number,
|
|||
|
|
default: 99
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 响应式变量
|
|||
|
|
const dragBarRef = ref(null)
|
|||
|
|
const scrollContainerRef = ref(null)
|
|||
|
|
const startY = ref(0)
|
|||
|
|
const startHeight = ref(props.initialHeight)
|
|||
|
|
const containerHeight = ref(props.initialHeight)
|
|||
|
|
// 新增:组件外层样式(动态绑定宽度、左侧偏移、z-index等)
|
|||
|
|
const wrapperStyle = ref({
|
|||
|
|
zIndex: props.zIndex,
|
|||
|
|
width: '0px',
|
|||
|
|
left: '0px'
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 核心方法:更新组件布局(宽度/左侧偏移),与父容器保持一致
|
|||
|
|
const updateContainerLayout = () => {
|
|||
|
|
nextTick(() => {
|
|||
|
|
// 校验父容器Ref是否有效
|
|||
|
|
console.log(props.parentRef)
|
|||
|
|
if (!props.parentRef) {
|
|||
|
|
console.warn('StickyDragContainer:传入的parentRef无效,请确保绑定了正确的DOM Ref')
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
// 获取父容器的实际布局信息(相对于视口的位置、宽度)
|
|||
|
|
const parentRect = props.parentRef.getBoundingClientRect()
|
|||
|
|
// 动态设置组件宽度(与父容器完全一致)、左侧偏移(与父容器左对齐)
|
|||
|
|
wrapperStyle.value = {
|
|||
|
|
zIndex: props.zIndex,
|
|||
|
|
width: `${parentRect.width}px`, // 继承父容器宽度
|
|||
|
|
left: `${parentRect.left}px` // 与父容器左对齐
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 拖拽相关方法(保持不变)
|
|||
|
|
const handleDragStart = (e) => {
|
|||
|
|
e.preventDefault()
|
|||
|
|
startY.value = e.clientY
|
|||
|
|
startHeight.value = scrollContainerRef.value?.getBoundingClientRect().height || containerHeight.value
|
|||
|
|
document.addEventListener('mousemove', handleDragMove)
|
|||
|
|
document.addEventListener('mouseup', handleDragEnd)
|
|||
|
|
}
|
|||
|
|
const handleDragMove = (e) => {
|
|||
|
|
e.preventDefault()
|
|||
|
|
const diffY = startY.value - e.clientY
|
|||
|
|
const newHeight = Math.max(props.minHeight, startHeight.value + diffY)
|
|||
|
|
containerHeight.value = newHeight
|
|||
|
|
}
|
|||
|
|
const handleDragEnd = () => {
|
|||
|
|
document.removeEventListener('mousemove', handleDragMove)
|
|||
|
|
document.removeEventListener('mouseup', handleDragEnd)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 生命周期:初始化+监听窗口缩放
|
|||
|
|
onMounted(() => {
|
|||
|
|
containerHeight.value = props.initialHeight
|
|||
|
|
updateContainerLayout() // 初始化时适配父容器布局
|
|||
|
|
// 新增:监听窗口缩放,父容器宽度变化时同步更新组件布局
|
|||
|
|
window.addEventListener('resize', updateContainerLayout)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
onUnmounted(() => {
|
|||
|
|
// 解绑所有事件,防止内存泄漏
|
|||
|
|
document.removeEventListener('mousemove', handleDragMove)
|
|||
|
|
document.removeEventListener('mouseup', handleDragEnd)
|
|||
|
|
window.removeEventListener('resize', updateContainerLayout) // 解绑窗口监听
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 监听props变化,动态更新
|
|||
|
|
watch([() => props.initialHeight, () => props.zIndex], () => {
|
|||
|
|
containerHeight.value = props.initialHeight
|
|||
|
|
wrapperStyle.value.zIndex = props.zIndex
|
|||
|
|
}, { immediate: true })
|
|||
|
|
|
|||
|
|
// 新增:如果父容器Ref变化,重新适配布局
|
|||
|
|
watch(() => props.parentRef, () => {
|
|||
|
|
updateContainerLayout()
|
|||
|
|
}, { deep: true, immediate: true })
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style scoped>
|
|||
|
|
/* 吸底外层容器:移除left/right:0,改为动态绑定;保留fixed+bottom:0核心吸底 */
|
|||
|
|
.sticky-drag-wrapper {
|
|||
|
|
position: fixed;
|
|||
|
|
bottom: 0; /* 仅保留吸底,宽度/左侧偏移由JS动态设置 */
|
|||
|
|
background: #ffffff;
|
|||
|
|
border-top: 1px solid #e6e6e6;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
transition: all 0.1s ease; /* 宽度/高度变化平滑过渡 */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 拖拽条样式(保持不变) */
|
|||
|
|
.drag-bar {
|
|||
|
|
height: 6px;
|
|||
|
|
background-color: #e6e6e6;
|
|||
|
|
cursor: n-resize;
|
|||
|
|
transition: background-color 0.2s ease;
|
|||
|
|
}
|
|||
|
|
.drag-bar:hover {
|
|||
|
|
background-color: #409eff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 内容滚动容器样式(保持不变) */
|
|||
|
|
.content-scroll-container {
|
|||
|
|
width: 100%; /* 继承外层wrapper的宽度(即父容器宽度) */
|
|||
|
|
overflow: auto;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
transition: height 0.1s ease;
|
|||
|
|
}
|
|||
|
|
</style>
|