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