Explorar el Código

完成针对截图配置功能

yuanmingze hace 1 mes
padre
commit
2e3aa293c4
Se han modificado 1 ficheros con 88 adiciones y 13 borrados
  1. 88 13
      src/pages-task/task-detail/index.vue

+ 88 - 13
src/pages-task/task-detail/index.vue

@@ -1,7 +1,12 @@
 <template>
-  <view class="task-detail-layout">
+  <view class="task-detail-layout" :class="{ 'task-detail-layout--export': isExportMode }">
     <!-- #ifdef H5 -->
-    <wd-navbar fixed placeholder safeAreaInsetTop title="任务详情" />
+    <wd-navbar
+      :fixed="!isExportMode"
+      :placeholder="!isExportMode"
+      safeAreaInsetTop
+      title="任务详情"
+    />
     <!-- #endif -->
 
     <view class="task-detail-page">
@@ -91,6 +96,7 @@ const EXPORT_MAX_WAIT_MS = 18_000
 const EXPORT_POLL_INTERVAL_MS = 120
 const EXPORT_STABLE_MS = 300
 const EXPORT_DOCUMENT_CLASS = 'task-detail-export'
+const EXPORT_HEIGHT_PROPERTY = '--task-detail-export-height'
 const QR_IMAGE_SELECTOR = '.share-detail-qrcode__image'
 
 const taskId = ref('')
@@ -111,6 +117,8 @@ let exportStartTs = 0
 let exportReadyNotified = false
 let exportFlowVersion = 0
 
+const isExportMode = computed(() => Boolean(exportToken.value))
+
 const isShareTaskDetail = (detail: TaskDetailResponse): detail is ShareTaskDetailResponse => {
   return SHARE_TASK_TYPES.has(String(detail.taskType))
 }
@@ -151,6 +159,38 @@ const getPageRoot = (): Element | null => {
   return document.querySelector('.task-detail-page')
 }
 
+const setExportDocumentMode = (enabled: boolean) => {
+  document.documentElement.classList.toggle(EXPORT_DOCUMENT_CLASS, enabled)
+  document.documentElement.style.removeProperty(EXPORT_HEIGHT_PROPERTY)
+  delete document.documentElement.dataset.taskDetailExportReady
+
+  if (enabled) window.scrollTo(0, 0)
+}
+
+/**
+ * Selenium 按 body.scrollHeight 调整浏览器窗口。导出前固定真实内容高度,避免窗口
+ * 调整后 vh 再次参与计算,造成页面高度漂移并重新出现内部滚动条。
+ */
+const freezeExportDocumentHeight = async () => {
+  if (!isExportMode.value) return
+
+  document.documentElement.style.removeProperty(EXPORT_HEIGHT_PROPERTY)
+  window.scrollTo(0, 0)
+  await waitForPaint()
+
+  const layout = document.querySelector<HTMLElement>('.task-detail-layout')
+  if (!layout) return
+
+  const layoutRect = layout.getBoundingClientRect()
+  const contentHeight = Math.ceil(
+    Math.max(layout.scrollHeight, layoutRect.height, layoutRect.bottom + window.scrollY)
+  )
+
+  document.documentElement.style.setProperty(EXPORT_HEIGHT_PROPERTY, `${contentHeight}px`)
+  document.documentElement.dataset.taskDetailExportReady = 'true'
+  await waitForPaint()
+}
+
 const getQrNaturalWidth = (): number => {
   const qrElement = getPageRoot()?.querySelector(QR_IMAGE_SELECTOR)
   const qrImage = qrElement?.matches('img')
@@ -259,6 +299,8 @@ const waitAndNotifyExportReady = async (flowVersion: number) => {
     if (flowVersion !== exportFlowVersion) return
 
     if (errorMessage.value || !taskDetail.value) {
+      await freezeExportDocumentHeight()
+      if (flowVersion !== exportFlowVersion) return
       notifyExportReadyOnce(false, false)
       return
     }
@@ -286,6 +328,8 @@ const waitAndNotifyExportReady = async (flowVersion: number) => {
         previousSnapshot = snapshot
         stableSince = Date.now()
       } else if (Date.now() - stableSince >= EXPORT_STABLE_MS) {
+        await freezeExportDocumentHeight()
+        if (flowVersion !== exportFlowVersion) return
         notifyExportReadyOnce(true, false)
         return
       }
@@ -298,6 +342,8 @@ const waitAndNotifyExportReady = async (flowVersion: number) => {
   }
 
   if (flowVersion === exportFlowVersion) {
+    await freezeExportDocumentHeight()
+    if (flowVersion !== exportFlowVersion) return
     notifyExportReadyOnce(false, true)
   }
 }
@@ -349,7 +395,7 @@ onLoad((options: PageLoadOptions = {}) => {
   taskId.value = String(options.id ?? options.taskId ?? '').trim()
   exportToken.value = String(options.exportToken ?? '').trim()
   // #ifdef H5
-  document.documentElement.classList.toggle(EXPORT_DOCUMENT_CLASS, Boolean(exportToken.value))
+  setExportDocumentMode(Boolean(exportToken.value))
   // #endif
   exportStartTs = Date.now()
   exportReadyNotified = false
@@ -382,26 +428,47 @@ onLoad((options: PageLoadOptions = {}) => {
 onUnload(() => {
   exportFlowVersion += 1
   // #ifdef H5
-  document.documentElement.classList.remove(EXPORT_DOCUMENT_CLASS)
+  setExportDocumentMode(false)
   // #endif
 })
 </script>
 
 <style>
 /* #ifdef H5 */
-/* 导出时让 body 的实际高度随完整内容增长,覆盖 uni-app 默认的 100% 高度链。 */
-html.task-detail-export,
-html.task-detail-export body,
-html.task-detail-export #app,
-html.task-detail-export uni-app,
-html.task-detail-export uni-page,
-html.task-detail-export uni-page-wrapper,
-html.task-detail-export uni-page-body {
+/*
+ * 截图时只保留浏览器文档滚动,解除 uni-app 默认的 100% 高度链和内部滚动容器。
+ * 后端读取 body.scrollHeight 后即可得到完整内容高度。
+ */
+:global(html.task-detail-export) {
   height: auto !important;
-  min-height: 100vh;
+  min-height: 0 !important;
+  max-height: none !important;
+  overflow-x: hidden !important;
+  overflow-y: visible !important;
+  scrollbar-width: none;
+}
+
+:global(html.task-detail-export body),
+:global(html.task-detail-export #app),
+:global(html.task-detail-export uni-app),
+:global(html.task-detail-export uni-page),
+:global(html.task-detail-export uni-page-wrapper),
+:global(html.task-detail-export uni-page-body) {
+  height: auto !important;
+  min-height: 0 !important;
   max-height: none !important;
   overflow: visible !important;
 }
+
+:global(html.task-detail-export body) {
+  min-height: var(--task-detail-export-height, 0px) !important;
+}
+
+:global(html.task-detail-export::-webkit-scrollbar),
+:global(html.task-detail-export body::-webkit-scrollbar) {
+  width: 0;
+  height: 0;
+}
 /* #endif */
 </style>
 
@@ -422,6 +489,14 @@ html.task-detail-export uni-page-body {
 .task-detail-page {
   min-height: calc(100vh - 44px);
 }
+
+.task-detail-layout--export,
+.task-detail-layout--export .task-detail-page {
+  height: auto !important;
+  min-height: 0 !important;
+  max-height: none !important;
+  overflow: visible !important;
+}
 /* #endif */
 
 .task-detail-state {