Răsfoiți Sursa

Fix 修复多级表头固定的问题

Yue 1 lună în urmă
părinte
comite
f3630f4073

+ 26 - 20
UI/VB.VUE/src/components/table/VbDataTable.vue

@@ -17,6 +17,7 @@ const props = withDefaults(
 	defineProps<{
 		columns: Header[]
 		columnsFun?: () => Header[]
+
 		tableTitle?: string
 		emptyTableText?: string
 		keyField?: string
@@ -335,7 +336,7 @@ const tableBoxClass = computed(() => {
 
 	return classStr
 })
-const _tableBoxStyle = ref(calcTableBoxStyle())
+const _tableBoxStyle = ref()
 const fixedColumn = computed(() => props.fixedNumber > 0 || props.fixedRightNumber > 0)
 const tableStyle = computed(() => {
 	let style = ""
@@ -635,21 +636,20 @@ function calcTableBoxStyle() {
 	if (props.tableBoxHeight) {
 		style.height = Number(props.tableBoxHeight) ? props.tableBoxHeight + "px" : props.tableBoxHeight
 	} else {
-		nextTick(() => {
-			const tableBoxAutoHeight =
-				document.documentElement.clientHeight -
-				(document.querySelector(".app-header")?.clientHeight ?? 0) -
-				(document.querySelector(".app-footer")?.clientHeight ?? 0) -
-				5
-			style.height = tableBoxAutoHeight + "px"
-		})
+		const tableBoxAutoHeight =
+			document.documentElement.clientHeight -
+			(document.querySelector(".app-header")?.clientHeight ?? 0) -
+			(document.querySelector(".app-footer")?.clientHeight ?? 0) -
+			5
+		style.height = tableBoxAutoHeight + "px"
 	}
 	if (props.tableBoxWidth) {
 		style.width = Number(props.tableBoxWidth) ? props.tableBoxWidth + "px" : props.tableBoxWidth
 	} else {
 		style.width = "100%"
 	}
-	return style
+	_tableBoxStyle.value = style
+	// return style
 }
 function query(query?: any) {
 	if (query) {
@@ -944,14 +944,21 @@ const onTreeToggle = (v: any) => {
 	}
 }
 const onFixedScrollX = () => {
-	leftFixedRef.value.className = leftFixedRef.value.className.replace("no-shadow", "")
-	rightFixedRef.value.className = rightFixedRef.value.className.replace("no-shadow", "")
+	if (leftFixedRef.value) {
+		leftFixedRef.value.className = leftFixedRef.value.className.replace("no-shadow", "")
+	}
+	if (rightFixedRef.value) {
+		rightFixedRef.value.className = rightFixedRef.value.className.replace("no-shadow", "")
+	}
 	if (tableResponsiveRef.value) {
 		const left = tableResponsiveRef.value.scrollLeft
-		if (left == 0) {
+		if (left == 0 && leftFixedRef.value) {
 			leftFixedRef.value.className += " no-shadow"
 		}
-		if (left >= tableRef.value.clientWidth - tableResponsiveRef.value.clientWidth) {
+		if (
+			rightFixedRef.value &&
+			left >= tableRef.value.clientWidth - tableResponsiveRef.value.clientWidth
+		) {
 			rightFixedRef.value.className += " no-shadow"
 		}
 	}
@@ -975,7 +982,7 @@ function BindInterEvent() {
 
 function initFixedColumns() {
 	if (fixedColumn.value) {
-		const tr = tableRef.value?.querySelector("tr")
+		const tr = tableRef.value?.querySelector("tbody tr")
 		if (tr && tr.children) {
 			let width = 0
 			if (leftFixedRef.value) {
@@ -1013,12 +1020,11 @@ function init() {
 		customSearch()
 	}
 	loadHandleBtns()
-	nextTick(() => {
-		initFixedColumns()
-		BindInterEvent()
-	})
+	initFixedColumns()
+	BindInterEvent()
+	calcTableBoxStyle()
 	window.addEventListener("resize", () => {
-		_tableBoxStyle.value = calcTableBoxStyle()
+		calcTableBoxStyle()
 	})
 }
 onMounted(init)

+ 86 - 49
UI/VB.VUE/src/components/table/partials/header/TableHeader.vue

@@ -2,6 +2,7 @@
 import symbolKeys from "@@@/table/symbol"
 import VbTr from "@@@/table/partials/header/HeaderTr.vue"
 import type { Sort, Header, HeaderEx } from "@@@/table/models"
+
 const propOpts = inject(symbolKeys.header, {
 	columns: ref([]),
 	hasCheckbox: true,
@@ -15,66 +16,102 @@ const { columns } = propOpts
 
 const sort = ref<Sort>({
 	label: propOpts.sortField,
-	order: propOpts.sortOrder
+	order: propOpts.sortOrder as "asc" | "desc"
 })
 
 const columnList = ref<HeaderEx[][]>([])
+const headerColumns = ref<HeaderEx[][]>([])
 const rowspan = computed(() => {
 	return columnList.value.length
 })
-const convert = (headers: Header[], deep: number): HeaderEx[] => {
-	if (!columnList.value[deep]) {
-		columnList.value.push([])
-	}
-	const list = columnList.value[deep]
-	const selfChildren = [] as HeaderEx[]
-	headers.forEach((v) => {
-		if (
-			v.visible == false ||
-			(v.children && v.children.find((v) => v.visible == undefined || v.visible) == null)
-		) {
-			return
-		}
-		const item = Object.assign({}, v, {
-			rowspan: (rs: number) => rs - deep,
-			colspan: v.children?.length ?? 1,
-			children: [] as HeaderEx[]
-		}) as HeaderEx
-		if (v.children) {
-			delete item.isSort
-			item.rowspan = 1
-			const _children = convert(v.children, deep + 1)
-			item.children = _children
+
+const convert = (headers: Header[], deep: number): HeaderEx[][] => {
+	const allLists: HeaderEx[][] = []
+
+	const recursion = (h: Header[], d: number): HeaderEx[] => {
+		if (!allLists[d]) {
+			allLists[d] = []
 		}
-		selfChildren.push(item)
-		list.push(item)
-	})
-	list.forEach((v) => {
-		//v.rowspan = v.rowspan != 1 ? (rs: number) => rs - deep : 1
-		v.colspan = v.colspan != 1 ? caclColspan(v.children) : 1
-	})
-	return selfChildren
-	function caclColspan(v?: HeaderEx[]): number {
-		let colspan = 0
-		if (v?.length) {
-			v.forEach((vv) => {
-				colspan += vv.children && vv.children.length ? caclColspan(vv.children) : vv.colspan
+		const currentLevelList = allLists[d]
+		const currentChildren: HeaderEx[] = []
+
+		h.forEach((v) => {
+			if (
+				v.visible === false ||
+				(v.children && v.children.every((child) => child.visible === false))
+			) {
+				return
+			}
+
+			const item: HeaderEx = Object.assign({}, v, {
+				rowspan: (rs: number) => rs - d,
+				colspan: v.children?.length ?? 1,
+				children: [] as HeaderEx[]
 			})
+
+			// 处理有子级的列:行合并为1,递归处理子级
+			if (v.children && v.children.length > 0) {
+				delete item.isSort
+				item.rowspan = 1 // 有子级的列,自身行合并为1
+				const childItems = recursion(v.children, d + 1)
+				item.children = childItems
+			}
+
+			currentChildren.push(item)
+			currentLevelList.push(item)
+		})
+
+		const calcColspan = (children?: HeaderEx[]): number => {
+			let totalColspan = 0
+			if (!children || children.length === 0) return totalColspan
+			children.forEach((child) => {
+				totalColspan +=
+					child.children && child.children.length > 0 ? calcColspan(child.children) : child.colspan
+			})
+			return totalColspan
 		}
-		return colspan
+
+		currentLevelList.forEach((item) => {
+			if (item.children && item.children.length > 0) {
+				item.colspan = calcColspan(item.children) // 子级列数求和作为当前列colspan
+			} else {
+				item.colspan = 1 // 无子级的列,列合并为1
+			}
+		})
+
+		return currentChildren
 	}
+
+	recursion(headers, deep)
+	return allLists
 }
-const headerColumns = computed(() => {
-	columnList.value = []
-	if (columns.value.filter((v) => v.children && v.children.length > 0).length > 0) {
-		convert(columns.value, 0)
-	} else {
-		columnList.value.push(
-			columns.value.filter((v) => v.visible == undefined || v.visible) as HeaderEx[]
-		)
+
+watch(
+	() => columns.value,
+	async () => {
+		await nextTick() // 等待DOM更新,确保计算准确
+		columnList.value = []
+		headerColumns.value = []
+
+		if (columns.value.some((v) => v.children && v.children.length > 0)) {
+			const convertedLists = convert(columns.value, 0)
+			columnList.value = convertedLists
+			headerColumns.value = convertedLists
+		} else {
+			const flatColumns = columns.value.filter((v) => v.visible !== false) as HeaderEx[]
+			flatColumns.forEach((item) => {
+				item.rowspan = rowspan.value
+				item.colspan = 1
+			})
+			columnList.value = [flatColumns]
+			headerColumns.value = [flatColumns]
+		}
+	},
+	{
+		immediate: true, // 初始化执行一次
+		deep: true // 深度监听columns(包括children变化)
 	}
-	return columnList.value
-})
+)
 </script>
 
 <template>
@@ -84,7 +121,7 @@ const headerColumns = computed(() => {
 				:rowspan="rowspan"
 				:columns="v"
 				:sort="sort"
-				:has-checkbox="i == 0 && propOpts.hasCheckbox"
+				:has-checkbox="i === 0 && propOpts.hasCheckbox"
 				:is-multiple-check="propOpts.isMultipleCheck"
 				:th-tr-class="propOpts.thTrClass">
 				<template v-for="(_, name) in $slots" #[name]="{ row }">