Răsfoiți Sursa

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

Yue 3 luni în urmă
părinte
comite
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)
                         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,
 VALUES (1, '9579f8780cf24ae2959d03d11482b18a', 'pc', 'iwb123', 'password,social', 'pc', 1800, 604800, 0, 0, 100, 1,
         SYSDATE(), 1, SYSDATE()),
         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());
         100, 1, SYSDATE(), 1, SYSDATE());

+ 36 - 30
UI/VAP_V3.VUE/src/components/table/VbDataTable.vue

@@ -17,6 +17,7 @@ const props = withDefaults(
 	defineProps<{
 	defineProps<{
 		columns: Header[]
 		columns: Header[]
 		columnsFun?: () => Header[]
 		columnsFun?: () => Header[]
+
 		tableTitle?: string
 		tableTitle?: string
 		emptyTableText?: string
 		emptyTableText?: string
 		keyField?: string
 		keyField?: string
@@ -335,7 +336,8 @@ const tableBoxClass = computed(() => {
 
 
 	return classStr
 	return classStr
 })
 })
-const _tableBoxStyle = ref(calcTableBoxStyle())
+// const _tableBoxStyle = ref(calcTableBoxStyle())
+const _tableBoxStyle = ref()
 const fixedColumn = computed(() => props.fixedNumber > 0 || props.fixedRightNumber > 0)
 const fixedColumn = computed(() => props.fixedNumber > 0 || props.fixedRightNumber > 0)
 const tableStyle = computed(() => {
 const tableStyle = computed(() => {
 	let style = ""
 	let style = ""
@@ -412,7 +414,7 @@ const defaultHandleFuns = {
 			})
 			})
 	},
 	},
 	handleExport: () => {
 	handleExport: () => {
-		console.log("TE")
+		// console.log("TE")
 		if (props.exportUrl) {
 		if (props.exportUrl) {
 			download(
 			download(
 				props.exportUrl,
 				props.exportUrl,
@@ -552,7 +554,6 @@ const isContentSlots = (name: string | number): boolean => {
 		}) != null
 		}) != null
 	)
 	)
 }
 }
-
 const loadHandleBtns = () => {
 const loadHandleBtns = () => {
 	toolbarHandleBtns.value = []
 	toolbarHandleBtns.value = []
 	const _handleFuns = Object.assign({}, defaultHandleFuns, props.handleFuns ?? {})
 	const _handleFuns = Object.assign({}, defaultHandleFuns, props.handleFuns ?? {})
@@ -636,21 +637,20 @@ function calcTableBoxStyle() {
 	if (props.tableBoxHeight) {
 	if (props.tableBoxHeight) {
 		style.height = Number(props.tableBoxHeight) ? props.tableBoxHeight + "px" : props.tableBoxHeight
 		style.height = Number(props.tableBoxHeight) ? props.tableBoxHeight + "px" : props.tableBoxHeight
 	} else {
 	} 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) {
 	if (props.tableBoxWidth) {
 		style.width = Number(props.tableBoxWidth) ? props.tableBoxWidth + "px" : props.tableBoxWidth
 		style.width = Number(props.tableBoxWidth) ? props.tableBoxWidth + "px" : props.tableBoxWidth
 	} else {
 	} else {
 		style.width = "100%"
 		style.width = "100%"
 	}
 	}
-	return style
+	_tableBoxStyle.value = style
+	// return style
 }
 }
 function query(query?: any) {
 function query(query?: any) {
 	if (query) {
 	if (query) {
@@ -672,8 +672,8 @@ function search(isReset = true) {
 function getQueryParams() {
 function getQueryParams() {
 	return innerQueryParams.value
 	return innerQueryParams.value
 }
 }
-function setQueryParams(params: any) {
-	innerQueryParams.value = params
+function setQueryParams(queryParams: any) {
+	return (innerQueryParams.value = queryParams)
 }
 }
 function getSelected() {
 function getSelected() {
 	return getSelecteds()[0]
 	return getSelecteds()[0]
@@ -883,8 +883,8 @@ const onReset = () => {
 }
 }
 const onTreeToggle = (v: any) => {
 const onTreeToggle = (v: any) => {
 	const row = toValue(v)
 	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")
 	const icon = tr?.querySelector(".icon")
 	if (!tr || !icon) {
 	if (!tr || !icon) {
 		return
 		return
@@ -893,21 +893,21 @@ const onTreeToggle = (v: any) => {
 		// 已有children可展开的直接展开,不在调用懒加载
 		// 已有children可展开的直接展开,不在调用懒加载
 		if (tr.className.search("hasChildren") >= 0) {
 		if (tr.className.search("hasChildren") >= 0) {
 			if (tr.className.search("tr-expand") >= 0 || !props.isLazySearch) {
 			if (tr.className.search("tr-expand") >= 0 || !props.isLazySearch) {
-				toggle(tr, icon, id)
+				toggle(tr, icon, tr_id)
 				return
 				return
 			}
 			}
 		}
 		}
-		remote(id).then((res) => {
+		remote(row[keyField.value]).then((res) => {
 			if (res == 1 && props.isLazySearch) {
 			if (res == 1 && props.isLazySearch) {
 				nextTick(() => {
 				nextTick(() => {
-					showChildren(id)
+					showChildren(tr_id)
 				})
 				})
 			}
 			}
 		})
 		})
 		tr.className = tr.className.replace("tr-collapse", "tr-expand")
 		tr.className = tr.className.replace("tr-collapse", "tr-expand")
 		icon.className = icon.className.replace("ki-add-folder", "ki-minus-folder")
 		icon.className = icon.className.replace("ki-add-folder", "ki-minus-folder")
 	} else {
 	} else {
-		toggle(tr, icon, id)
+		toggle(tr, icon, tr_id)
 	}
 	}
 	function toggle(_tr: HTMLElement, _icon: HTMLElement, id: string) {
 	function toggle(_tr: HTMLElement, _icon: HTMLElement, id: string) {
 		if (_tr.className.search("tr-expand") >= 0) {
 		if (_tr.className.search("tr-expand") >= 0) {
@@ -945,14 +945,21 @@ const onTreeToggle = (v: any) => {
 	}
 	}
 }
 }
 const onFixedScrollX = () => {
 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) {
 	if (tableResponsiveRef.value) {
 		const left = tableResponsiveRef.value.scrollLeft
 		const left = tableResponsiveRef.value.scrollLeft
-		if (left == 0) {
+		if (left == 0 && leftFixedRef.value) {
 			leftFixedRef.value.className += " no-shadow"
 			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"
 			rightFixedRef.value.className += " no-shadow"
 		}
 		}
 	}
 	}
@@ -976,7 +983,7 @@ function BindInterEvent() {
 
 
 function initFixedColumns() {
 function initFixedColumns() {
 	if (fixedColumn.value) {
 	if (fixedColumn.value) {
-		const tr = tableRef.value?.querySelector("tr")
+		const tr = tableRef.value?.querySelector("tbody tr")
 		if (tr && tr.children) {
 		if (tr && tr.children) {
 			let width = 0
 			let width = 0
 			if (leftFixedRef.value) {
 			if (leftFixedRef.value) {
@@ -1014,12 +1021,11 @@ function init() {
 		customSearch()
 		customSearch()
 	}
 	}
 	loadHandleBtns()
 	loadHandleBtns()
-	nextTick(() => {
-		initFixedColumns()
-		BindInterEvent()
-	})
+	initFixedColumns()
+	BindInterEvent()
+	calcTableBoxStyle()
 	window.addEventListener("resize", () => {
 	window.addEventListener("resize", () => {
-		_tableBoxStyle.value = calcTableBoxStyle()
+		calcTableBoxStyle()
 	})
 	})
 }
 }
 onMounted(init)
 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 symbolKeys from "@@@/table/symbol"
 import VbTr from "@@@/table/partials/header/HeaderTr.vue"
 import VbTr from "@@@/table/partials/header/HeaderTr.vue"
 import type { Sort, Header, HeaderEx } from "@@@/table/models"
 import type { Sort, Header, HeaderEx } from "@@@/table/models"
+
 const propOpts = inject(symbolKeys.header, {
 const propOpts = inject(symbolKeys.header, {
 	columns: ref([]),
 	columns: ref([]),
 	hasCheckbox: true,
 	hasCheckbox: true,
@@ -15,66 +16,102 @@ const { columns } = propOpts
 
 
 const sort = ref<Sort>({
 const sort = ref<Sort>({
 	label: propOpts.sortField,
 	label: propOpts.sortField,
-	order: propOpts.sortOrder
+	order: propOpts.sortOrder as "asc" | "desc"
 })
 })
 
 
 const columnList = ref<HeaderEx[][]>([])
 const columnList = ref<HeaderEx[][]>([])
+const headerColumns = ref<HeaderEx[][]>([])
 const rowspan = computed(() => {
 const rowspan = computed(() => {
 	return columnList.value.length
 	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>
 </script>
 
 
 <template>
 <template>
@@ -84,7 +121,7 @@ const headerColumns = computed(() => {
 				:rowspan="rowspan"
 				:rowspan="rowspan"
 				:columns="v"
 				:columns="v"
 				:sort="sort"
 				:sort="sort"
-				:has-checkbox="i == 0 && propOpts.hasCheckbox"
+				:has-checkbox="i === 0 && propOpts.hasCheckbox"
 				:is-multiple-check="propOpts.isMultipleCheck"
 				:is-multiple-check="propOpts.isMultipleCheck"
 				:th-tr-class="propOpts.thTrClass">
 				:th-tr-class="propOpts.thTrClass">
 				<template v-for="(_, name) in $slots" #[name]="{ row }">
 				<template v-for="(_, name) in $slots" #[name]="{ row }">