Files
pqs-9100_client/frontend/src/components/Grid/index.vue

154 lines
3.9 KiB
Vue
Raw Normal View History

2024-08-26 20:05:04 +08:00
<template>
2024-10-22 14:47:25 +08:00
<div :style='style'>
2024-08-26 20:05:04 +08:00
<slot></slot>
</div>
</template>
2024-10-22 14:47:25 +08:00
<script setup lang='ts' name='Grid'>
import type { BreakPoint } from './interface/index'
2024-08-26 20:05:04 +08:00
type Props = {
cols?: number | Record<BreakPoint, number>;
collapsed?: boolean;
collapsedRows?: number;
gap?: [number, number] | number;
};
const props = withDefaults(defineProps<Props>(), {
cols: () => ({ xs: 1, sm: 2, md: 2, lg: 3, xl: 4 }),
collapsed: false,
collapsedRows: 1,
2024-10-22 14:47:25 +08:00
gap: 0,
})
2024-08-26 20:05:04 +08:00
2024-10-22 14:47:25 +08:00
onBeforeMount(() => props.collapsed && findIndex())
2024-08-26 20:05:04 +08:00
onMounted(() => {
2024-10-22 14:47:25 +08:00
resize({ target: { innerWidth: window.innerWidth } } as unknown as UIEvent)
window.addEventListener('resize', resize)
})
2024-08-26 20:05:04 +08:00
onActivated(() => {
2024-10-22 14:47:25 +08:00
resize({ target: { innerWidth: window.innerWidth } } as unknown as UIEvent)
window.addEventListener('resize', resize)
})
2024-08-26 20:05:04 +08:00
onUnmounted(() => {
2024-10-22 14:47:25 +08:00
window.removeEventListener('resize', resize)
})
2024-08-26 20:05:04 +08:00
onDeactivated(() => {
2024-10-22 14:47:25 +08:00
window.removeEventListener('resize', resize)
})
2024-08-26 20:05:04 +08:00
// 监听屏幕变化
const resize = (e: UIEvent) => {
2024-10-22 14:47:25 +08:00
let width = (e.target as Window).innerWidth
2024-08-26 20:05:04 +08:00
switch (!!width) {
case width < 768:
2024-10-22 14:47:25 +08:00
breakPoint.value = 'xs'
break
2024-08-26 20:05:04 +08:00
case width >= 768 && width < 992:
2024-10-22 14:47:25 +08:00
breakPoint.value = 'sm'
break
2024-08-26 20:05:04 +08:00
case width >= 992 && width < 1200:
2024-10-22 14:47:25 +08:00
breakPoint.value = 'md'
break
2024-08-26 20:05:04 +08:00
case width >= 1200 && width < 1920:
2024-10-22 14:47:25 +08:00
breakPoint.value = 'lg'
break
2024-08-26 20:05:04 +08:00
case width >= 1920:
2024-10-22 14:47:25 +08:00
breakPoint.value = 'xl'
break
2024-08-26 20:05:04 +08:00
}
2024-10-22 14:47:25 +08:00
}
2024-08-26 20:05:04 +08:00
// 注入 gap 间距
2024-10-22 14:47:25 +08:00
provide('gap', Array.isArray(props.gap) ? props.gap[0] : props.gap)
2024-08-26 20:05:04 +08:00
// 注入响应式断点
2024-10-22 14:47:25 +08:00
let breakPoint = ref<BreakPoint>('xl')
provide('breakPoint', breakPoint)
2024-08-26 20:05:04 +08:00
// 注入要开始折叠的 index
2024-10-22 14:47:25 +08:00
const hiddenIndex = ref(-1)
provide('shouldHiddenIndex', hiddenIndex)
2024-08-26 20:05:04 +08:00
// 注入 cols
const gridCols = computed(() => {
2024-10-22 14:47:25 +08:00
if (typeof props.cols === 'object') return props.cols[breakPoint.value] ?? props.cols
return props.cols
})
provide('cols', gridCols)
2024-08-26 20:05:04 +08:00
// 寻找需要开始折叠的字段 index
2024-10-22 14:47:25 +08:00
const slots = useSlots().default!()
2024-08-26 20:05:04 +08:00
const findIndex = () => {
2024-10-22 14:47:25 +08:00
let fields: VNodeArrayChildren = []
let suffix: VNode | null = null
2024-08-26 20:05:04 +08:00
slots.forEach((slot: any) => {
// suffix
2024-10-22 14:47:25 +08:00
if (typeof slot.type === 'object' && slot.type.name === 'GridItem' && slot.props?.suffix !== undefined) suffix = slot
2024-08-26 20:05:04 +08:00
// slot children
2024-10-22 14:47:25 +08:00
if (typeof slot.type === 'symbol' && Array.isArray(slot.children)) fields.push(...slot.children)
})
2024-08-26 20:05:04 +08:00
// 计算 suffix 所占用的列
2024-10-22 14:47:25 +08:00
let suffixCols = 0
2024-08-26 20:05:04 +08:00
if (suffix) {
suffixCols =
2024-10-22 14:47:25 +08:00
((suffix as VNode).props![breakPoint.value]?.span ?? (suffix as VNode).props?.span ?? 1) +
((suffix as VNode).props![breakPoint.value]?.offset ?? (suffix as VNode).props?.offset ?? 0)
2024-08-26 20:05:04 +08:00
}
try {
2024-10-22 14:47:25 +08:00
let find = false
2024-08-26 20:05:04 +08:00
fields.reduce((prev = 0, current, index) => {
prev +=
2024-10-22 14:47:25 +08:00
((current as VNode)!.props![breakPoint.value]?.span ?? (current as VNode)!.props?.span ?? 1) +
((current as VNode)!.props![breakPoint.value]?.offset ?? (current as VNode)!.props?.offset ?? 0)
2024-10-24 12:14:24 +08:00
if (Number(prev) >= props.collapsedRows * gridCols.value - suffixCols) {
2024-10-22 14:47:25 +08:00
hiddenIndex.value = index
find = true
throw 'find it'
2024-08-26 20:05:04 +08:00
}
2024-10-22 14:47:25 +08:00
return prev
}, 0)
if (!find) hiddenIndex.value = -1
2024-08-26 20:05:04 +08:00
} catch (e) {
2024-11-14 18:26:34 +08:00
2024-08-26 20:05:04 +08:00
}
2024-10-22 14:47:25 +08:00
}
2024-08-26 20:05:04 +08:00
// 断点变化时执行 findIndex
watch(
2024-10-22 14:47:25 +08:00
() => breakPoint.value,
() => {
if (props.collapsed) findIndex()
},
)
2024-08-26 20:05:04 +08:00
// 监听 collapsed
watch(
2024-10-22 14:47:25 +08:00
() => props.collapsed,
value => {
if (value) return findIndex()
hiddenIndex.value = -1
},
)
2024-08-26 20:05:04 +08:00
// 设置间距
const gridGap = computed(() => {
2024-10-22 14:47:25 +08:00
if (typeof props.gap === 'number') return `${props.gap}px`
if (Array.isArray(props.gap)) return `${props.gap[1]}px ${props.gap[0]}px`
return 'unset'
})
2024-08-26 20:05:04 +08:00
// 设置 style
const style = computed(() => {
return {
2024-10-22 14:47:25 +08:00
display: 'grid',
2024-08-26 20:05:04 +08:00
gridGap: gridGap.value,
2024-10-22 14:47:25 +08:00
gridTemplateColumns: `repeat(${gridCols.value}, minmax(0, 1fr))`,
}
})
2024-08-26 20:05:04 +08:00
2024-10-22 14:47:25 +08:00
defineExpose({ breakPoint })
2024-08-26 20:05:04 +08:00
</script>