feat: 新增物料管理看板功能及多项优化

新增物料管理看板功能,包含统计卡片和图表展示
优化物料选择器组件,支持分页和搜索功能
重构物料详情展示组件,支持动态加载数据
添加多个ECharts图表组件用于数据可视化
完善出入库和采购单相关功能,增加在途数量显示
修复若干界面显示问题和交互逻辑
This commit is contained in:
砂糖
2026-01-31 14:21:49 +08:00
parent 48e75676c5
commit 42f6f83c3a
17 changed files with 1278 additions and 115 deletions

View File

@@ -0,0 +1,149 @@
<!-- @/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>