/** * 表格高亮 Composable * 用于在 ContentRenderer 渲染的表格中高亮当前产品型号对应的列 */ export function useTableHighlight() { /** * 高亮包含指定文本的表格列 * @param highlightText 要高亮的文本内容 * @param containerSelector 容器选择器,默认为 '.prose' * @param highlightClass 高亮CSS类名,默认为 'highlighted-model-column' */ function highlightTableColumns( highlightText: string, containerSelector: string = ".full-table-screen .prose", highlightClass: string = "highlighted-model-column" ) { if (!highlightText?.trim()) return; const containers = document.querySelectorAll(containerSelector); containers.forEach((container) => { const tables = container.querySelectorAll("table"); tables.forEach((table) => { const rows = table.querySelectorAll("tbody tr, tr"); const targetColumnIndexes: number[] = []; // 首先遍历所有行,找到包含目标文本的列索引 rows.forEach((row) => { const cells = row.querySelectorAll("td, th"); cells.forEach((cell, columnIndex) => { const textContent = cell.textContent?.trim() || ""; if (textContent === highlightText.trim()) { if (!targetColumnIndexes.includes(columnIndex)) { targetColumnIndexes.push(columnIndex); } } }); }); // 清除所有列的高亮 rows.forEach((row) => { const cells = row.querySelectorAll("td, th"); cells.forEach((cell) => { cell.classList.remove(highlightClass); }); }); // 高亮目标列的所有单元格 if (targetColumnIndexes.length > 0) { rows.forEach((row) => { const cells = row.querySelectorAll("td, th"); targetColumnIndexes.forEach((columnIndex) => { if (cells[columnIndex]) { cells[columnIndex].classList.add(highlightClass); } }); }); // 在小屏幕上自动滚动到高亮列 scrollToHighlightedColumn(table, targetColumnIndexes[0]); } }); }); } /** * 在小屏幕上滚动到高亮列 * @param table 表格元素 * @param columnIndex 列索引 */ function scrollToHighlightedColumn(table: Element, columnIndex: number) { if (typeof window === "undefined") return; // 只在小屏幕上执行滚动 if (window.innerWidth > 768) return; const firstRow = table.querySelector("tr"); if (!firstRow) return; const targetCell = firstRow.children[columnIndex] as HTMLElement; if (!targetCell) return; // 找到可滚动的父容器 const scrollContainer = table.closest(".prose")?.parentElement; if (!scrollContainer) return; // 延迟执行滚动,确保样式已应用 setTimeout(() => { const cellRect = targetCell.getBoundingClientRect(); const containerRect = scrollContainer.getBoundingClientRect(); // 计算需要滚动的距离 const scrollLeft = targetCell.offsetLeft - containerRect.width / 2 + cellRect.width / 2; scrollContainer.scrollTo({ left: Math.max(0, scrollLeft), behavior: "smooth", }); }, 200); } /** * 添加表格响应式包装器 * @param table 表格元素 */ function wrapTableForResponsive(table: Element) { if (table.parentElement?.classList.contains("table-responsive-wrapper")) { return; // 已经包装过了 } const wrapper = document.createElement("div"); wrapper.className = "table-responsive-wrapper overflow-x-auto bg-zinc-900 rounded-lg"; wrapper.style.cssText = ` overflow-x: auto; -webkit-overflow-scrolling: touch; border-radius: 8px; background: rgb(24 24 27); margin: 1rem 0; `; // 确保表格有足够的宽度 (table as HTMLElement).style.cssText = ` min-width: 1200px; width: 100%; margin: 0; border-radius: 0; table-layout: auto; `; // 为表格单元格设置合适的宽度 const cells = table.querySelectorAll("th, td"); cells.forEach((cell, index) => { const htmlCell = cell as HTMLElement; if (index === 0) { // 第一列给更多宽度 htmlCell.style.minWidth = "200px"; } else { htmlCell.style.minWidth = "150px"; } htmlCell.style.maxWidth = "300px"; htmlCell.style.whiteSpace = "nowrap"; htmlCell.style.overflow = "hidden"; htmlCell.style.textOverflow = "ellipsis"; }); // 添加滚动指示器 const scrollIndicator = document.createElement("div"); scrollIndicator.className = "scroll-indicator text-xs text-zinc-400 mb-2 block md:hidden"; table.parentNode?.insertBefore(wrapper, table); wrapper.appendChild(scrollIndicator); wrapper.appendChild(table); // 监听滚动,隐藏指示器 wrapper.addEventListener("scroll", () => { if (wrapper.scrollLeft > 10) { scrollIndicator.style.display = "none"; } }); // 添加自定义滚动条样式 const style = document.createElement("style"); style.textContent = ` .table-responsive-wrapper::-webkit-scrollbar { height: 12px; } .table-responsive-wrapper::-webkit-scrollbar-track { background: rgb(39 39 42); border-radius: 6px; } .table-responsive-wrapper::-webkit-scrollbar-thumb { background: rgb(34 197 94); border-radius: 6px; border: 2px solid rgb(39 39 42); } .table-responsive-wrapper::-webkit-scrollbar-thumb:hover { background: rgb(34 197 94 / 0.8); } @keyframes fadeInOut { 0%, 100% { opacity: 0.6; } 50% { opacity: 1; } } `; if (!document.head.querySelector("#table-scroll-styles")) { style.id = "table-scroll-styles"; document.head.appendChild(style); } } /** * 初始化表格列高亮功能 * @param highlightText 要高亮的文本内容(响应式) * @param delay 延迟执行时间(毫秒),默认500ms */ function initTableHighlight( highlightText: Ref | ComputedRef, delay: number = 500 ) { // 页面挂载后执行高亮 onMounted(() => { nextTick(() => { setTimeout(() => { // 为所有表格添加响应式包装器 const tables = document.querySelectorAll(".full-table-screen .prose table"); tables.forEach(wrapTableForResponsive); // 执行高亮 highlightTableColumns(unref(highlightText)); }, delay); }); }); // 监听文本变化,重新高亮 watch(highlightText, (newText) => { nextTick(() => { setTimeout(() => { highlightTableColumns(newText); }, 100); }); }); // 监听窗口大小变化,重新调整表格 if (typeof window !== "undefined") { const handleResize = debounce(() => { const tables = document.querySelectorAll(".full-table-screen .prose table"); tables.forEach(wrapTableForResponsive); highlightTableColumns(unref(highlightText)); }, 300); window.addEventListener("resize", handleResize); onUnmounted(() => { window.removeEventListener("resize", handleResize); }); } } /** * 防抖函数 * @param func 要防抖的函数 * @param delay 延迟时间 */ function debounce(func: Function, delay: number) { let timeoutId: ReturnType; return (...args: any[]) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => func.apply(null, args), delay); }; } return { highlightTableColumns, initTableHighlight, scrollToHighlightedColumn, wrapTableForResponsive, }; }