Hanye官网
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

useTableHighlight.ts 7.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. /**
  2. * 表格高亮 Composable
  3. * 用于在 ContentRenderer 渲染的表格中高亮当前产品型号对应的列
  4. */
  5. export function useTableHighlight() {
  6. /**
  7. * 高亮包含指定文本的表格列
  8. * @param highlightText 要高亮的文本内容
  9. * @param containerSelector 容器选择器,默认为 '.prose'
  10. * @param highlightClass 高亮CSS类名,默认为 'highlighted-model-column'
  11. */
  12. function highlightTableColumns(
  13. highlightText: string,
  14. containerSelector: string = ".full-table-screen .prose",
  15. highlightClass: string = "highlighted-model-column"
  16. ) {
  17. if (!highlightText?.trim()) return;
  18. const containers = document.querySelectorAll(containerSelector);
  19. containers.forEach((container) => {
  20. const tables = container.querySelectorAll("table");
  21. tables.forEach((table) => {
  22. const rows = table.querySelectorAll("tbody tr, tr");
  23. const targetColumnIndexes: number[] = [];
  24. // 首先遍历所有行,找到包含目标文本的列索引
  25. rows.forEach((row) => {
  26. const cells = row.querySelectorAll("td, th");
  27. cells.forEach((cell, columnIndex) => {
  28. const textContent = cell.textContent?.trim() || "";
  29. if (textContent === highlightText.trim()) {
  30. if (!targetColumnIndexes.includes(columnIndex)) {
  31. targetColumnIndexes.push(columnIndex);
  32. }
  33. }
  34. });
  35. });
  36. // 清除所有列的高亮
  37. rows.forEach((row) => {
  38. const cells = row.querySelectorAll("td, th");
  39. cells.forEach((cell) => {
  40. cell.classList.remove(highlightClass);
  41. });
  42. });
  43. // 高亮目标列的所有单元格
  44. if (targetColumnIndexes.length > 0) {
  45. rows.forEach((row) => {
  46. const cells = row.querySelectorAll("td, th");
  47. targetColumnIndexes.forEach((columnIndex) => {
  48. if (cells[columnIndex]) {
  49. cells[columnIndex].classList.add(highlightClass);
  50. }
  51. });
  52. });
  53. // 在小屏幕上自动滚动到高亮列
  54. scrollToHighlightedColumn(table, targetColumnIndexes[0]);
  55. }
  56. });
  57. });
  58. }
  59. /**
  60. * 在小屏幕上滚动到高亮列
  61. * @param table 表格元素
  62. * @param columnIndex 列索引
  63. */
  64. function scrollToHighlightedColumn(table: Element, columnIndex: number) {
  65. if (typeof window === "undefined") return;
  66. // 只在小屏幕上执行滚动
  67. if (window.innerWidth > 768) return;
  68. const firstRow = table.querySelector("tr");
  69. if (!firstRow) return;
  70. const targetCell = firstRow.children[columnIndex] as HTMLElement;
  71. if (!targetCell) return;
  72. // 找到可滚动的父容器
  73. const scrollContainer = table.closest(".prose")?.parentElement;
  74. if (!scrollContainer) return;
  75. // 延迟执行滚动,确保样式已应用
  76. setTimeout(() => {
  77. const cellRect = targetCell.getBoundingClientRect();
  78. const containerRect = scrollContainer.getBoundingClientRect();
  79. // 计算需要滚动的距离
  80. const scrollLeft =
  81. targetCell.offsetLeft - containerRect.width / 2 + cellRect.width / 2;
  82. scrollContainer.scrollTo({
  83. left: Math.max(0, scrollLeft),
  84. behavior: "smooth",
  85. });
  86. }, 200);
  87. }
  88. /**
  89. * 添加表格响应式包装器
  90. * @param table 表格元素
  91. */
  92. function wrapTableForResponsive(table: Element) {
  93. if (table.parentElement?.classList.contains("table-responsive-wrapper")) {
  94. return; // 已经包装过了
  95. }
  96. const wrapper = document.createElement("div");
  97. wrapper.className =
  98. "table-responsive-wrapper overflow-x-auto bg-zinc-900 rounded-lg";
  99. wrapper.style.cssText = `
  100. overflow-x: auto;
  101. -webkit-overflow-scrolling: touch;
  102. border-radius: 8px;
  103. background: rgb(24 24 27);
  104. margin: 1rem 0;
  105. `;
  106. // 确保表格有足够的宽度
  107. (table as HTMLElement).style.cssText = `
  108. min-width: 1200px;
  109. width: 100%;
  110. margin: 0;
  111. border-radius: 0;
  112. table-layout: auto;
  113. `;
  114. // 为表格单元格设置合适的宽度
  115. const cells = table.querySelectorAll("th, td");
  116. cells.forEach((cell, index) => {
  117. const htmlCell = cell as HTMLElement;
  118. if (index === 0) {
  119. // 第一列给更多宽度
  120. htmlCell.style.minWidth = "200px";
  121. } else {
  122. htmlCell.style.minWidth = "150px";
  123. }
  124. htmlCell.style.maxWidth = "300px";
  125. htmlCell.style.whiteSpace = "nowrap";
  126. htmlCell.style.overflow = "hidden";
  127. htmlCell.style.textOverflow = "ellipsis";
  128. });
  129. // 添加滚动指示器
  130. const scrollIndicator = document.createElement("div");
  131. scrollIndicator.className =
  132. "scroll-indicator text-xs text-zinc-400 mb-2 block md:hidden";
  133. table.parentNode?.insertBefore(wrapper, table);
  134. wrapper.appendChild(scrollIndicator);
  135. wrapper.appendChild(table);
  136. // 监听滚动,隐藏指示器
  137. wrapper.addEventListener("scroll", () => {
  138. if (wrapper.scrollLeft > 10) {
  139. scrollIndicator.style.display = "none";
  140. }
  141. });
  142. // 添加自定义滚动条样式
  143. const style = document.createElement("style");
  144. style.textContent = `
  145. .table-responsive-wrapper::-webkit-scrollbar {
  146. height: 12px;
  147. }
  148. .table-responsive-wrapper::-webkit-scrollbar-track {
  149. background: rgb(39 39 42);
  150. border-radius: 6px;
  151. }
  152. .table-responsive-wrapper::-webkit-scrollbar-thumb {
  153. background: rgb(34 197 94);
  154. border-radius: 6px;
  155. border: 2px solid rgb(39 39 42);
  156. }
  157. .table-responsive-wrapper::-webkit-scrollbar-thumb:hover {
  158. background: rgb(34 197 94 / 0.8);
  159. }
  160. @keyframes fadeInOut {
  161. 0%, 100% { opacity: 0.6; }
  162. 50% { opacity: 1; }
  163. }
  164. `;
  165. if (!document.head.querySelector("#table-scroll-styles")) {
  166. style.id = "table-scroll-styles";
  167. document.head.appendChild(style);
  168. }
  169. }
  170. /**
  171. * 初始化表格列高亮功能
  172. * @param highlightText 要高亮的文本内容(响应式)
  173. * @param delay 延迟执行时间(毫秒),默认500ms
  174. */
  175. function initTableHighlight(
  176. highlightText: Ref<string> | ComputedRef<string>,
  177. delay: number = 500
  178. ) {
  179. // 页面挂载后执行高亮
  180. onMounted(() => {
  181. nextTick(() => {
  182. setTimeout(() => {
  183. // 为所有表格添加响应式包装器
  184. const tables = document.querySelectorAll(".full-table-screen .prose table");
  185. tables.forEach(wrapTableForResponsive);
  186. // 执行高亮
  187. highlightTableColumns(unref(highlightText));
  188. }, delay);
  189. });
  190. });
  191. // 监听文本变化,重新高亮
  192. watch(highlightText, (newText) => {
  193. nextTick(() => {
  194. setTimeout(() => {
  195. highlightTableColumns(newText);
  196. }, 100);
  197. });
  198. });
  199. // 监听窗口大小变化,重新调整表格
  200. if (typeof window !== "undefined") {
  201. const handleResize = debounce(() => {
  202. const tables = document.querySelectorAll(".full-table-screen .prose table");
  203. tables.forEach(wrapTableForResponsive);
  204. highlightTableColumns(unref(highlightText));
  205. }, 300);
  206. window.addEventListener("resize", handleResize);
  207. onUnmounted(() => {
  208. window.removeEventListener("resize", handleResize);
  209. });
  210. }
  211. }
  212. /**
  213. * 防抖函数
  214. * @param func 要防抖的函数
  215. * @param delay 延迟时间
  216. */
  217. function debounce(func: Function, delay: number) {
  218. let timeoutId: ReturnType<typeof setTimeout>;
  219. return (...args: any[]) => {
  220. clearTimeout(timeoutId);
  221. timeoutId = setTimeout(() => func.apply(null, args), delay);
  222. };
  223. }
  224. return {
  225. highlightTableColumns,
  226. initTableHighlight,
  227. scrollToHighlightedColumn,
  228. wrapTableForResponsive,
  229. };
  230. }