Forráskód Böngészése

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

Yue 3 hónapja
szülő
commit
358e07020d

+ 1 - 1
SERVER/VberAdminPlusV3/.script/sql/admin.sql

@@ -1022,5 +1022,5 @@ INSERT INTO sys_client (id, client_id, client_key, client_secret, grant_type, de
                         status, del_flag, create_org, create_by, create_time, update_by, update_time)
 VALUES (1, '9579f8780cf24ae2959d03d11482b18a', 'pc', 'iwb123', 'password,social', 'pc', 1800, 604800, 0, 0, 100, 1,
         SYSDATE(), 1, SYSDATE()),
-       (2, '35aee70ae7224eb9a48bc527955ddedc', 'app', 'iwb123', 'password,sms,social', 'android', 1800, 604800, 0, 0,
+       (2, '35aee70ae7224eb9a48bc527955ddedc', 'app', 'iwb123', 'password,sms,social', 'android', 604800, 2592000, 0, 0,
         100, 1, SYSDATE(), 1, SYSDATE());

+ 36 - 30
UI/VAP_V3.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,8 @@ const tableBoxClass = computed(() => {
 
 	return classStr
 })
-const _tableBoxStyle = ref(calcTableBoxStyle())
+// const _tableBoxStyle = ref(calcTableBoxStyle())
+const _tableBoxStyle = ref()
 const fixedColumn = computed(() => props.fixedNumber > 0 || props.fixedRightNumber > 0)
 const tableStyle = computed(() => {
 	let style = ""
@@ -412,7 +414,7 @@ const defaultHandleFuns = {
 			})
 	},
 	handleExport: () => {
-		console.log("TE")
+		// console.log("TE")
 		if (props.exportUrl) {
 			download(
 				props.exportUrl,
@@ -552,7 +554,6 @@ const isContentSlots = (name: string | number): boolean => {
 		}) != null
 	)
 }
-
 const loadHandleBtns = () => {
 	toolbarHandleBtns.value = []
 	const _handleFuns = Object.assign({}, defaultHandleFuns, props.handleFuns ?? {})
@@ -636,21 +637,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) {
@@ -672,8 +672,8 @@ function search(isReset = true) {
 function getQueryParams() {
 	return innerQueryParams.value
 }
-function setQueryParams(params: any) {
-	innerQueryParams.value = params
+function setQueryParams(queryParams: any) {
+	return (innerQueryParams.value = queryParams)
 }
 function getSelected() {
 	return getSelecteds()[0]
@@ -883,8 +883,8 @@ const onReset = () => {
 }
 const onTreeToggle = (v: any) => {
 	const row = toValue(v)
-	const id = "tr_" + row[keyField.value]
-	const tr = tableBoxRef.value?.querySelector(`#${id}`)
+	const tr_id = "tr_" + row[keyField.value]
+	const tr = tableBoxRef.value?.querySelector(`#${tr_id}`)
 	const icon = tr?.querySelector(".icon")
 	if (!tr || !icon) {
 		return
@@ -893,21 +893,21 @@ const onTreeToggle = (v: any) => {
 		// 已有children可展开的直接展开,不在调用懒加载
 		if (tr.className.search("hasChildren") >= 0) {
 			if (tr.className.search("tr-expand") >= 0 || !props.isLazySearch) {
-				toggle(tr, icon, id)
+				toggle(tr, icon, tr_id)
 				return
 			}
 		}
-		remote(id).then((res) => {
+		remote(row[keyField.value]).then((res) => {
 			if (res == 1 && props.isLazySearch) {
 				nextTick(() => {
-					showChildren(id)
+					showChildren(tr_id)
 				})
 			}
 		})
 		tr.className = tr.className.replace("tr-collapse", "tr-expand")
 		icon.className = icon.className.replace("ki-add-folder", "ki-minus-folder")
 	} else {
-		toggle(tr, icon, id)
+		toggle(tr, icon, tr_id)
 	}
 	function toggle(_tr: HTMLElement, _icon: HTMLElement, id: string) {
 		if (_tr.className.search("tr-expand") >= 0) {
@@ -945,14 +945,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"
 		}
 	}
@@ -976,7 +983,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) {
@@ -1014,12 +1021,11 @@ function init() {
 		customSearch()
 	}
 	loadHandleBtns()
-	nextTick(() => {
-		initFixedColumns()
-		BindInterEvent()
-	})
+	initFixedColumns()
+	BindInterEvent()
+	calcTableBoxStyle()
 	window.addEventListener("resize", () => {
-		_tableBoxStyle.value = calcTableBoxStyle()
+		calcTableBoxStyle()
 	})
 }
 onMounted(init)

+ 86 - 49
UI/VAP_V3.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 }">