Browse Source

调整PDF预览组件、对接签名推送和服务单编辑功能

yangguanjin 3 weeks ago
parent
commit
d93a7cba5a

+ 21 - 0
src/api/ApiRouter/taskOrder.ts

@@ -10,6 +10,7 @@ import {
   getUserGroupUserList,
   recheckApi,
   getMajorIssuesAuditList,
+  updateTaskOrder,
 } from '@/api/task'
 import { getPendingVerificationListApi } from '@/api/pendingVerification'
 import { getPendingPreparationListApi } from '@/api/pendingPreparation'
@@ -28,6 +29,7 @@ import {
   getPipePendingVerificationListApi,
   getPipePendingPreparationListApi,
   getPipeMajorIssuesAuditList,
+  updatePipeTaskOrder,
 } from '@/api/pipe/pipeTaskOrder'
 import {
   getBoilerTaskConfirmPage,
@@ -42,6 +44,7 @@ import {
   getBoilerPendingVerificationListApi,
   getBoilerPendingPreparationListApi,
   getBoilerMajorIssuesAuditList,
+  updateBoilerTaskOrder,
 } from '@/api/boiler/boilerTaskOrder'
 
 type Adapter = {
@@ -52,6 +55,7 @@ type Adapter = {
 
 export enum TaskOrderFuncName {
   ConfirmList,
+  UpdateTaskOrder,
   TaskEquipList,
   CheckEquipTaskList,
   TaskConfirm,
@@ -86,6 +90,23 @@ const map = {
       outputAdapter: null,
     },
   },
+  [TaskOrderFuncName.UpdateTaskOrder]: {
+    [EquipmentType.BOILER]: {
+      inputAdapter: null,
+      reqFunction: updateBoilerTaskOrder,
+      outputAdapter: null,
+    },
+    [EquipmentType.PIPE]: {
+      inputAdapter: null,
+      reqFunction: updatePipeTaskOrder,
+      outputAdapter: null,
+    },
+    [EquipmentType.CONTAINER]: {
+      inputAdapter: null,
+      reqFunction: updateTaskOrder,
+      outputAdapter: null,
+    },
+  },
   [TaskOrderFuncName.TaskEquipList]: {
     [EquipmentType.BOILER]: {
       inputAdapter: null,

+ 5 - 0
src/api/boiler/boilerTaskOrder.ts

@@ -10,6 +10,11 @@ export const getBoilerTaskEquipmentList = (params: any) => {
   return httpGet('/pressure2/boiler-task-order/order-item/page', params)
 }
 
+// 更新锅炉任务单
+export const updateBoilerTaskOrder = (data: any) => {
+  return httpPUT(`/pressure2/boiler-task-order/update`, data)
+}
+
 // 任务分配接口:获取检验员任务单下的设备单详情
 export const getBoilerCheckerEquipmentDetailById = (params: any) => {
   return httpGet('/pressure2/boiler-task-order/order-item/get', params)

+ 5 - 0
src/api/pipe/pipeTaskOrder.ts

@@ -5,6 +5,11 @@ export const getPipeTaskConfirmPage = (params: any) => {
   return httpGet('/pressure2/pipe-task-order/page', params)
 }
 
+// 更新承压任务单
+export const updatePipeTaskOrder = (data: any) => {
+  return httpPUT(`/pressure2/pipe-task-order/update`, data)
+}
+
 // 获取任务设备列表
 export const getPipeTaskEquipmentList = (params: any) => {
   return httpGet('/pressure2/pipe-task-order/order-item/page', params)

+ 7 - 0
src/api/sign.ts

@@ -65,6 +65,13 @@ export const pushTaskOrder = (data: any) => {
   return httpPost('/pressure/task-order/service-from/push', data)
 }
 
+/**
+ * 推送任务单
+ */
+export const pushPressure2TaskOrder = (data: any) => {
+  return httpPost('/pressure2/boiler-task-order-sign-file/create', data)
+}
+
 /**
  * 更新服务单数据
  */

+ 197 - 354
src/components/PDFViewer/index.vue

@@ -8,16 +8,46 @@
     </view>
     <template v-else>
       <!-- #ifdef H5 -->
-      <view class="pdf-viewport" :id="viewportId" :style="{ height: viewerHeight }">
-        <view class="pdf-transform-layer" :style="transformStyle">
-          <view class="pdf-container" :id="containerId"></view>
+      <scroll-view
+        scroll-y
+        scroll-x
+        class="pdf-content-area"
+        :style="{ height: viewerHeight }"
+      >
+        <view
+          v-if="contentReady"
+          class="pdf-scroll-wrapper"
+          :style="scrollWrapperStyle"
+        >
+          <view class="pdf-scale-layer" :style="scaleLayerStyle">
+            <view class="pdf-container" :id="containerId"></view>
+          </view>
         </view>
-      </view>
-      <view class="pdf-zoom-bar">
+      </scroll-view>
+      <!-- #endif -->
+      <!-- #ifndef H5 -->
+      <scroll-view
+        scroll-y
+        scroll-x
+        class="pdf-content-area"
+        :style="{ height: viewerHeight }"
+      >
+        <view v-if="contentReady" class="pdf-scroll-wrapper" :style="scrollWrapperStyle">
+          <view class="pdf-scale-layer" :style="scaleLayerStyle">
+            <image
+              :src="imageSrc"
+              mode="widthFix"
+              class="pdf-image"
+              :style="{ width: contentWidth + 'px' }"
+            />
+          </view>
+        </view>
+      </scroll-view>
+      <!-- #endif -->
+      <view v-if="contentReady" class="pdf-zoom-bar">
         <view class="zoom-btn" @click="zoomOut">
           <text class="zoom-btn-text">−</text>
         </view>
-        <text class="zoom-label">{{ zoomPercent }}%</text>
         <view class="zoom-btn" @click="zoomIn">
           <text class="zoom-btn-text">+</text>
         </view>
@@ -25,33 +55,6 @@
           <text class="zoom-btn-text">重置</text>
         </view>
       </view>
-      <!-- #endif -->
-      <!-- #ifndef H5 -->
-      <view v-if="imageSrc" class="pdf-image-area" :style="{ height: viewerHeight }">
-        <movable-area class="pdf-movable-area">
-          <movable-view
-            v-if="imageWidth > 0 && imageHeight > 0"
-            direction="all"
-            :scale="true"
-            scale-min="0.5"
-            scale-max="5"
-            :scale-value="1"
-            class="pdf-movable-view"
-            :style="{ width: imageWidth + 'px', height: imageHeight + 'px' }"
-          >
-            <image
-              :src="imageSrc"
-              mode="aspectFit"
-              class="pdf-image"
-              :style="{ width: imageWidth + 'px' }"
-            />
-          </movable-view>
-        </movable-area>
-      </view>
-      <view v-else class="pdf-placeholder">
-        <text class="placeholder-text">暂无PDF预览</text>
-      </view>
-      <!-- #endif -->
     </template>
   </view>
 </template>
@@ -80,267 +83,109 @@ const errorMsg = ref('')
 const pageCount = ref(0)
 let pdfDoc: any = null
 
+const contentReady = ref(false)
+const contentWidth = ref(0)
+const contentHeight = ref(0)
+const firstPageHeight = ref(0)
+const fitScale = ref(1)
+const currentScale = ref(1)
+
 const imageSrc = ref('')
-const imageWidth = ref(0)
-const imageHeight = ref(0)
 
 let isMounted = false
 
 // #ifdef H5
 import * as pdfjsLib from 'pdfjs-dist/legacy/build/pdf.mjs'
 import pdfjsWorker from 'pdfjs-dist/legacy/build/pdf.worker.min.mjs?url'
+// #endif
 
-const viewportId = ref(`pdf-vp-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`)
-
-const MIN_ZOOM = 0.5
 const MAX_ZOOM = 5
 const ZOOM_STEP = 0.25
 
-const zoomScale = ref(1)
-const panX = ref(0)
-const panY = ref(0)
-
-let contentHeight = 0
-
-const zoomPercent = computed(() => Math.round(zoomScale.value * 100))
+const scaleMin = computed(() => Math.max(0.5, fitScale.value))
 
-const transformStyle = computed(() => ({
-  transform: `translate(${panX.value}px, ${panY.value}px) scale(${zoomScale.value})`,
+const scaleLayerStyle = computed(() => ({
+  width: `${contentWidth.value}px`,
+  height: `${contentHeight.value}px`,
+  transform: `scale(${currentScale.value})`,
   transformOrigin: '0 0',
 }))
 
-let touchStartDistance = 0
-let touchStartScale = 1
-let touchStartPanX = 0
-let touchStartPanY = 0
-let pinchCenterX = 0
-let pinchCenterY = 0
-let isPinching = false
-let isPanning = false
-let isScrolling = false
-let singleTouchStartX = 0
-let singleTouchStartY = 0
-let scrollMoveStartY = 0
-let touchStartClientY = 0
-let lastTapTime = 0
-let scrollVelocity = 0
-let momentumAnimId = 0
-let lastTouchY = 0
-let lastTouchTime = 0
+const scrollWrapperStyle = computed(() => ({
+  width: `${Math.ceil(contentWidth.value * currentScale.value)}px`,
+  height: `${Math.ceil(contentHeight.value * currentScale.value)}px`,
+  position: 'relative' as const,
+}))
 
-function getTouchDistance(touches: TouchList): number {
-  const dx = touches[0].clientX - touches[1].clientX
-  const dy = touches[0].clientY - touches[1].clientY
-  return Math.sqrt(dx * dx + dy * dy)
+function zoomIn() {
+  const newScale = Math.min(MAX_ZOOM, currentScale.value + ZOOM_STEP)
+  currentScale.value = newScale
 }
 
-function getTouchCenter(touches: TouchList): { x: number; y: number } {
-  return {
-    x: (touches[0].clientX + touches[1].clientX) / 2,
-    y: (touches[0].clientY + touches[1].clientY) / 2,
-  }
+function zoomOut() {
+  const newScale = Math.max(scaleMin.value, currentScale.value - ZOOM_STEP)
+  currentScale.value = newScale
 }
 
-function clampScroll() {
-  if (zoomScale.value > 1.01) return
-  panX.value = 0
-  const viewport = document.getElementById(viewportId.value)
-  if (!viewport) return
-  const vh = viewport.clientHeight
-  const maxScroll = Math.max(0, contentHeight - vh)
-  panY.value = Math.max(-maxScroll, Math.min(0, panY.value))
+function resetZoom() {
+  currentScale.value = fitScale.value
 }
 
-function onTouchStart(e: TouchEvent) {
-  cancelAnimationFrame(momentumAnimId)
-  scrollVelocity = 0
-
-  const viewport = document.getElementById(viewportId.value)
-  if (!viewport) return
-  const rect = viewport.getBoundingClientRect()
-
-  if (e.touches.length === 2) {
-    isPinching = true
-    isPanning = false
-    isScrolling = false
-    touchStartDistance = getTouchDistance(e.touches)
-    touchStartScale = zoomScale.value
-    touchStartPanX = panX.value
-    touchStartPanY = panY.value
-    const center = getTouchCenter(e.touches)
-    pinchCenterX = center.x - rect.left
-    pinchCenterY = center.y - rect.top
-    e.preventDefault()
-  } else if (e.touches.length === 1) {
-    const now = Date.now()
-    touchStartClientY = e.touches[0].clientY
-    if (now - lastTapTime < 300) {
-      if (zoomScale.value > 1.05) {
-        zoomScale.value = 1
-        panX.value = 0
-        clampScroll()
-      } else {
-        const cx = e.touches[0].clientX - rect.left
-        const cy = e.touches[0].clientY - rect.top
-        const newScale = 2
-        const ratio = newScale / zoomScale.value
-        panX.value = cx - cx * ratio
-        panY.value = cy - (cy - panY.value) * ratio
-        zoomScale.value = newScale
-      }
-      e.preventDefault()
-    } else if (zoomScale.value > 1.01) {
-      isPanning = true
-      isPinching = false
-      isScrolling = false
-      singleTouchStartX = e.touches[0].clientX
-      singleTouchStartY = e.touches[0].clientY
-      touchStartPanX = panX.value
-      touchStartPanY = panY.value
-    } else {
-      isScrolling = true
-      isPanning = false
-      isPinching = false
-      scrollMoveStartY = e.touches[0].clientY
-      lastTouchY = scrollMoveStartY
-      lastTouchTime = Date.now()
-    }
-    lastTapTime = now
+const getContainerSize = async (): Promise<{ width: number; height: number }> => {
+  // #ifdef H5
+  await nextTick()
+  const el = document.querySelector('.pdf-content-area')
+  if (el) {
+    const rect = el.getBoundingClientRect()
+    return { width: rect.width, height: rect.height }
   }
-}
+  // #endif
 
-function onTouchMove(e: TouchEvent) {
-  if (isPinching && e.touches.length >= 2) {
-    e.preventDefault()
-    const currentDistance = getTouchDistance(e.touches)
-    const newScale = Math.min(
-      MAX_ZOOM,
-      Math.max(MIN_ZOOM, touchStartScale * (currentDistance / touchStartDistance)),
-    )
-    const ratio = newScale / touchStartScale
-    panX.value = pinchCenterX - (pinchCenterX - touchStartPanX) * ratio
-    panY.value = pinchCenterY - (pinchCenterY - touchStartPanY) * ratio
-    zoomScale.value = newScale
-  } else if (isPanning && e.touches.length === 1) {
-    e.preventDefault()
-    panX.value = touchStartPanX + (e.touches[0].clientX - singleTouchStartX)
-    panY.value = touchStartPanY + (e.touches[0].clientY - singleTouchStartY)
-  } else if (isScrolling && e.touches.length === 1) {
-    e.preventDefault()
-    const y = e.touches[0].clientY
-    if (Math.abs(y - touchStartClientY) > 10) {
-      lastTapTime = 0
-    }
-    const now = Date.now()
-    const dt = now - lastTouchTime
-    if (dt > 0) {
-      scrollVelocity = ((lastTouchY - y) / dt) * 16
-    }
-    lastTouchY = y
-    lastTouchTime = now
-    panY.value += y - scrollMoveStartY
-    scrollMoveStartY = y
-    clampScroll()
+  const sysInfo = uni.getSystemInfoSync()
+  const h = props.viewerHeight
+  let height = sysInfo.windowHeight
+  if (h.endsWith('vh')) {
+    height = (parseFloat(h) / 100) * sysInfo.windowHeight
+  } else if (h.endsWith('px')) {
+    height = parseFloat(h)
   }
+  return { width: sysInfo.windowWidth, height }
 }
 
-function onTouchEnd(e: TouchEvent) {
-  if (e.touches.length === 0) {
-    if (isScrolling) {
-      isScrolling = false
-      startMomentumScroll()
-    }
-    isPinching = false
-    isPanning = false
-    if (zoomScale.value >= 0.95 && zoomScale.value < 1.05) {
-      zoomScale.value = 1
-      panX.value = 0
-      clampScroll()
-    }
-  } else if (e.touches.length === 1 && isPinching) {
-    isPinching = false
-    if (zoomScale.value > 1.01) {
-      isPanning = true
-      singleTouchStartX = e.touches[0].clientX
-      singleTouchStartY = e.touches[0].clientY
-      touchStartPanX = panX.value
-      touchStartPanY = panY.value
-    }
-  }
-}
+const applyFitScale = async () => {
+  if (!contentReady.value) return
 
-function startMomentumScroll() {
-  const friction = 0.95
-  function step() {
-    if (Math.abs(scrollVelocity) < 0.5) return
-    panY.value += scrollVelocity
-    scrollVelocity *= friction
-    clampScroll()
-    momentumAnimId = requestAnimationFrame(step)
-  }
-  momentumAnimId = requestAnimationFrame(step)
-}
+  const { width: containerWidth, height: containerHeight } = await getContainerSize()
 
-function onWheel(e: WheelEvent) {
-  e.preventDefault()
-  const viewport = document.getElementById(viewportId.value)
-  if (!viewport) return
-  const rect = viewport.getBoundingClientRect()
+  const scaleByHeight = containerHeight > 0 && firstPageHeight.value > 0
+    ? containerHeight / firstPageHeight.value
+    : 1
+  const scaleByWidth = containerWidth > 0 && contentWidth.value > 0
+    ? containerWidth / contentWidth.value
+    : 1
 
-  if (e.ctrlKey || e.metaKey) {
-    const delta = -e.deltaY
-    const factor = delta > 0 ? 1.1 : 1 / 1.1
-    const newScale = Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, zoomScale.value * factor))
-    const cx = e.clientX - rect.left
-    const cy = e.clientY - rect.top
-    const ratio = newScale / zoomScale.value
-    panX.value = cx - (cx - panX.value) * ratio
-    panY.value = cy - (cy - panY.value) * ratio
-    zoomScale.value = newScale
-    if (newScale <= 1.01) {
-      panX.value = 0
-      clampScroll()
-    }
-  } else {
-    if (zoomScale.value > 1.01) {
-      panY.value -= e.deltaY
-      panX.value -= e.deltaX
-    } else {
-      panY.value -= e.deltaY
-      clampScroll()
-    }
-  }
+  fitScale.value = Math.min(scaleByHeight, scaleByWidth)
+  currentScale.value = fitScale.value
 }
 
-function applyZoom(newScale: number, cx: number, cy: number) {
-  const ratio = newScale / zoomScale.value
-  panX.value = cx - (cx - panX.value) * ratio
-  panY.value = cy - (cy - panY.value) * ratio
-  zoomScale.value = newScale
-  if (newScale <= 1.01) {
-    panX.value = 0
-    clampScroll()
+// #ifdef H5
+const calculateContentSize = async () => {
+  if (!pdfDoc) return
+  const containerWidth = uni.getSystemInfoSync().windowWidth - 32
+  let totalHeight = 0
+  for (let i = 1; i <= pdfDoc.numPages; i++) {
+    const page = await pdfDoc.getPage(i)
+    const unscaledViewport = page.getViewport({ scale: 1 })
+    const scale = containerWidth / unscaledViewport.width
+    const viewport = page.getViewport({ scale })
+    totalHeight += viewport.height
+    if (i === 1) {
+      firstPageHeight.value = viewport.height
+    }
   }
-}
-
-function zoomIn() {
-  const viewport = document.getElementById(viewportId.value)
-  if (!viewport) return
-  const rect = viewport.getBoundingClientRect()
-  applyZoom(Math.min(MAX_ZOOM, zoomScale.value + ZOOM_STEP), rect.width / 2, rect.height / 2)
-}
-
-function zoomOut() {
-  const viewport = document.getElementById(viewportId.value)
-  if (!viewport) return
-  const rect = viewport.getBoundingClientRect()
-  applyZoom(Math.max(MIN_ZOOM, zoomScale.value - ZOOM_STEP), rect.width / 2, rect.height / 2)
-}
-
-function resetZoom() {
-  zoomScale.value = 1
-  panX.value = 0
-  panY.value = 0
+  contentWidth.value = containerWidth
+  contentHeight.value = totalHeight
+  contentReady.value = true
 }
 
 const renderAllPages = async () => {
@@ -353,7 +198,6 @@ const renderAllPages = async () => {
 
     const containerWidth = uni.getSystemInfoSync().windowWidth - 32
     const dpr = window.devicePixelRatio || 1
-    let totalHeight = 0
 
     for (let i = 1; i <= pdfDoc.numPages; i++) {
       const page = await pdfDoc.getPage(i)
@@ -373,16 +217,52 @@ const renderAllPages = async () => {
       await page.render({ canvasContext: context!, viewport }).promise
 
       container.appendChild(canvas)
-      totalHeight += viewport.height
     }
-
-    contentHeight = totalHeight
   } catch (error) {
     console.error('PDF页面渲染失败:', error)
     errorMsg.value = 'PDF页面渲染失败'
     emit('error', 'PDF页面渲染失败')
   }
 }
+
+let pinchStartDistance = 0
+let pinchStartScale = 1
+
+function getTouchDistance(touches: TouchList): number {
+  const dx = touches[0].clientX - touches[1].clientX
+  const dy = touches[0].clientY - touches[1].clientY
+  return Math.sqrt(dx * dx + dy * dy)
+}
+
+function handleTouchStart(e: TouchEvent) {
+  if (e.touches.length === 2) {
+    e.preventDefault()
+    pinchStartDistance = getTouchDistance(e.touches)
+    pinchStartScale = currentScale.value
+  }
+}
+
+function handleTouchMove(e: TouchEvent) {
+  if (e.touches.length === 2) {
+    e.preventDefault()
+    const currentDistance = getTouchDistance(e.touches)
+    const newScale = Math.min(
+      MAX_ZOOM,
+      Math.max(scaleMin.value, pinchStartScale * (currentDistance / pinchStartDistance)),
+    )
+    currentScale.value = newScale
+  }
+}
+
+function handleWheel(e: WheelEvent) {
+  if (e.ctrlKey || e.metaKey) {
+    e.preventDefault()
+    const delta = -e.deltaY
+    const factor = delta > 0 ? 1.1 : 1 / 1.1
+    const newScale = Math.min(MAX_ZOOM, Math.max(scaleMin.value, currentScale.value * factor))
+    currentScale.value = newScale
+  }
+}
 // #endif
 
 const loadPDFFromArrayBuffer = async (data: ArrayBuffer) => {
@@ -401,6 +281,9 @@ const loadPDFFromArrayBuffer = async (data: ArrayBuffer) => {
     pdfDoc = pdf
     pageCount.value = pdf.numPages
 
+    await calculateContentSize()
+    await applyFitScale()
+
     loading.value = false
     emit('loaded', pdf.numPages)
 
@@ -418,19 +301,27 @@ const loadPDFFromArrayBuffer = async (data: ArrayBuffer) => {
     const base64 = btoa(binaryString)
     imageSrc.value = `data:application/pdf;base64,${base64}`
 
+    const screenWidth = uni.getSystemInfoSync().windowWidth - 32
+    contentWidth.value = screenWidth
+
     uni.getImageInfo({
-      src: `data:application/pdf;base64,${base64}`,
-      success: (imageInfo) => {
-        const screenWidth = uni.getSystemInfoSync().windowWidth - 32
-        imageWidth.value = screenWidth
-        imageHeight.value = (screenWidth * imageInfo.height) / imageInfo.width
+      src: imageSrc.value,
+      success: async (imageInfo) => {
+        contentHeight.value = (screenWidth * imageInfo.height) / imageInfo.width
+        firstPageHeight.value = contentHeight.value
+        contentReady.value = true
+        await applyFitScale()
         pageCount.value = 1
         loading.value = false
         emit('loaded', 1)
       },
-      fail: () => {
-        loading.value = false
+      fail: async () => {
+        contentHeight.value = Math.floor(screenWidth * 1.414)
+        firstPageHeight.value = contentHeight.value
+        contentReady.value = true
+        await applyFitScale()
         pageCount.value = 1
+        loading.value = false
         emit('loaded', 1)
       },
     })
@@ -453,15 +344,12 @@ const resetViewer = () => {
   pageCount.value = 0
   imageSrc.value = ''
   errorMsg.value = ''
-
-  // #ifdef H5
-  zoomScale.value = 1
-  panX.value = 0
-  panY.value = 0
-  contentHeight = 0
-  scrollVelocity = 0
-  cancelAnimationFrame(momentumAnimId)
-  // #endif
+  contentReady.value = false
+  contentWidth.value = 0
+  contentHeight.value = 0
+  firstPageHeight.value = 0
+  fitScale.value = 1
+  currentScale.value = 1
 }
 
 watch(
@@ -489,16 +377,18 @@ watch(
   },
 )
 
+let scrollEl: HTMLElement | null = null
+
 onMounted(async () => {
   isMounted = true
 
   // #ifdef H5
-  const viewport = document.getElementById(viewportId.value)
-  if (viewport) {
-    viewport.addEventListener('touchstart', onTouchStart, { passive: false })
-    viewport.addEventListener('touchmove', onTouchMove, { passive: false })
-    viewport.addEventListener('touchend', onTouchEnd, { passive: false })
-    viewport.addEventListener('wheel', onWheel, { passive: false })
+  await nextTick()
+  scrollEl = document.querySelector('.pdf-content-area')
+  if (scrollEl) {
+    scrollEl.addEventListener('touchstart', handleTouchStart, { passive: false })
+    scrollEl.addEventListener('touchmove', handleTouchMove, { passive: false })
+    scrollEl.addEventListener('wheel', handleWheel, { passive: false })
   }
   // #endif
 
@@ -515,13 +405,11 @@ onUnmounted(() => {
   isMounted = false
 
   // #ifdef H5
-  cancelAnimationFrame(momentumAnimId)
-  const viewport = document.getElementById(viewportId.value)
-  if (viewport) {
-    viewport.removeEventListener('touchstart', onTouchStart)
-    viewport.removeEventListener('touchmove', onTouchMove)
-    viewport.removeEventListener('touchend', onTouchEnd)
-    viewport.removeEventListener('wheel', onWheel)
+  if (scrollEl) {
+    scrollEl.removeEventListener('touchstart', handleTouchStart)
+    scrollEl.removeEventListener('touchmove', handleTouchMove)
+    scrollEl.removeEventListener('wheel', handleWheel)
+    scrollEl = null
   }
   // #endif
 
@@ -569,22 +457,20 @@ defineExpose({
   }
 }
 
-/* #ifdef H5 */
-.pdf-viewport {
-  position: relative;
+.pdf-content-area {
   width: 100%;
-  overflow: hidden;
-  touch-action: none;
-  user-select: none;
-  -webkit-user-select: none;
   background-color: #f5f5f5;
   border-radius: 8px;
 }
 
-.pdf-transform-layer {
+.pdf-scale-layer {
+  position: absolute;
+  top: 0;
+  left: 0;
   will-change: transform;
 }
 
+/* #ifdef H5 */
 .pdf-container {
   display: flex;
   flex-direction: column;
@@ -595,6 +481,12 @@ defineExpose({
   }
 }
 
+/* #endif */
+
+.pdf-image {
+  display: block;
+}
+
 .pdf-zoom-bar {
   display: flex;
   align-items: center;
@@ -602,7 +494,7 @@ defineExpose({
   gap: 8px;
   padding: 6px 12px;
   margin: 8px auto 0;
-  background-color: rgba(0, 0, 0, 0.65);
+  background-color: rgba(0, 0, 0, 0.55);
   border-radius: 20px;
   width: fit-content;
 }
@@ -615,7 +507,6 @@ defineExpose({
   height: 32px;
   background-color: rgba(255, 255, 255, 0.2);
   border-radius: 50%;
-  cursor: pointer;
   transition: background-color 0.2s;
 
   &:active {
@@ -631,59 +522,11 @@ defineExpose({
 
 .zoom-btn-reset {
   width: auto;
-  padding: 0 10px;
+  padding: 0 8px;
   border-radius: 12px;
-  margin-left: 4px;
 
   .zoom-btn-text {
-    font-size: 12px;
-  }
-}
-
-.zoom-label {
-  font-size: 13px;
-  color: #fff;
-  min-width: 40px;
-  text-align: center;
-}
-/* #endif */
-
-/* #ifndef H5 */
-.pdf-image-area {
-  width: 100%;
-  background-color: #f5f5f5;
-  border-radius: 8px;
-  overflow: hidden;
-}
-
-.pdf-movable-area {
-  width: 100%;
-  height: 100%;
-}
-
-.pdf-movable-view {
-  display: flex;
-  align-items: flex-start;
-  justify-content: center;
-}
-
-.pdf-image {
-  display: block;
-}
-/* #endif */
-
-.pdf-placeholder {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  width: 100%;
-  height: 200px;
-  background-color: #f5f5f5;
-  border-radius: 8px;
-
-  .placeholder-text {
-    font-size: 14px;
-    color: #999;
+    font-size: 11px;
   }
 }
 </style>

+ 1 - 2
src/components/SpreadDesigner/SpreadPDFConverter.vue

@@ -992,8 +992,8 @@ function renderPictureCell(designerInstance) {
             (cellVal.endsWith('.jpg') || cellVal.endsWith('.png'))
           ) {
             // 图片渲染
-            console.log('picture.....', cellVal)
             const fileUrl = buildFileUrl(cellVal)
+            cell.value('')
             const base64 = await urlToBase64(fileUrl)
             let x = 0
             let y = 0
@@ -1023,7 +1023,6 @@ function renderPictureCell(designerInstance) {
             const b = cell.borderTop()?.style | 1
             const c = cell.borderRight()?.style | 1
             const d = cell.borderBottom()?.style | 1
-            console.log(cellVal)
             const addPictureShape = sheet.shapes.addPictureShape(
               cellVal,
               base64,

+ 3 - 1
src/components/SpreadDesigner/SpreadPDFViewer.vue

@@ -10,7 +10,7 @@
         @save-file="handleSpreadSaveFile"
       />
     </view>
-    <PDFViewer ref="pdfViewerRef" :source="pdfSource" />
+    <PDFViewer ref="pdfViewerRef" :source="pdfSource" :viewer-height="viewerHeight" />
   </view>
 </template>
 
@@ -24,12 +24,14 @@ interface Props {
   businessConfig?: any
   templateData?: any
   templateBlob?: string
+  viewerHeight?: string
 }
 
 const props = withDefaults(defineProps<Props>(), {
   businessConfig: null,
   templateData: null,
   templateBlob: '',
+  viewerHeight: '95vh',
 })
 
 const emit = defineEmits<{

+ 10 - 0
src/pages.json

@@ -123,6 +123,16 @@
         "disableScroll": true
       }
     },
+    {
+      "path": "pages/serviceOrderDetail/serviceOrderEditor",
+      "type": "page",
+      "layout": "default",
+      "style": {
+        "navigationBarTitleText": "",
+        "navigationStyle": "custom",
+        "disableScroll": true
+      }
+    },
     {
       "path": "pages/sign/index",
       "type": "page",

+ 15 - 35
src/pages/home/index.vue

@@ -34,17 +34,16 @@
       <!-- 待办事项标题 -->
       <view class="todo-title-box">
         <text class="todo-title">我的待办事项</text>
-        <picker
+        <wd-picker
+          style="z-index: 999;"
           class="equip-picker"
-          :range="equipmentOptions"
-          range-key="label"
-          @change="onEquipTypeChange"
-        >
-          <view class="picker-value">
-            <text>{{ currentEquipLabel }}</text>
-            <text class="picker-arrow">▼</text>
-          </view>
-        </picker>
+          :model-value="equipType"
+          :columns="equipmentOptions"
+          label-key="label"
+          value-key="value"
+          placeholder="请选择"
+          @confirm="onEquipTypeChange"
+        />
       </view>
 
       <!-- 待办卡片列表 -->
@@ -92,6 +91,7 @@
 
 <script lang="ts" setup>
 import { ref, reactive, computed, onMounted, onUnmounted } from 'vue'
+import { onShow, onLoad } from '@dcloudio/uni-app'
 import { useUserStore } from '@/store/user'
 import { useConfigStore } from '@/store/config'
 import { EquipmentType } from '@/utils/dictMap'
@@ -129,15 +129,13 @@ const equipmentOptions = [
   // { label: '压力容器', value: EquipmentType.CONTAINER },
 ]
 
-const currentEquipLabel = computed(() => {
-  return equipmentOptions.find((item) => item.value === equipType.value)?.label || '请选择'
+onShow(() => {
+  refreshAllCards()
 })
 
-const onEquipTypeChange = (e: any) => {
-  const index = e.detail.value
-  const selected = equipmentOptions[index]
-  if (selected && selected.value !== equipType.value) {
-    configStore.switchEquipType(selected.value)
+const onEquipTypeChange = ({ value }: { value: string }) => {
+  if (value && value !== equipType.value) {
+    configStore.switchEquipType(value as any)
     uni.reLaunch({ url: '/pages/home/index' })
   }
 }
@@ -176,7 +174,6 @@ const refreshAllCards = () => {
     })
   }
 }
-
 onMounted(() => {
   getWindowWidth()
   getUserId()
@@ -558,23 +555,6 @@ const onRefresh = () => {
   flex-shrink: 0;
 }
 
-.picker-value {
-  display: flex;
-  flex-direction: row;
-  gap: 4px;
-  align-items: center;
-  padding: 4px 8px;
-  font-size: 14px;
-  color: #333;
-  background-color: #f5f5f5;
-  border-radius: 4px;
-}
-
-.picker-arrow {
-  font-size: 10px;
-  color: #999;
-}
-
 .todo-title {
   font-size: 18px;
   color: rgb(51, 51, 51);

+ 136 - 0
src/pages/serviceOrderDetail/serviceOrderEditor.vue

@@ -0,0 +1,136 @@
+<route lang="json5" type="page">
+{
+  layout: 'default',
+  style: {
+    navigationBarTitleText: '',
+    navigationStyle: 'custom',
+    disableScroll: true,
+  },
+}
+</route>
+
+<template>
+  <div>
+    <SpreadDesignerGeneric
+      :businessConfig="businessConfig"
+      :templateData="templateData"
+      :templateBlob="templateBlob"
+      @save="handleSave"
+      @cancel="handleCancel"
+    />
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref } from 'vue'
+import SpreadDesignerGeneric from '@/components/SpreadDesigner/spreadDesignerGeneric.vue'
+import { onLoad } from '@dcloudio/uni-app'
+import { buildFileUrl } from '@/utils/index'
+import { useConfigStore } from '@/store/config'
+import { getStandardTemplate } from '@/api/index'
+import { getDynamicTbVal, saveDynamicTbVal } from '@/api/task'
+
+const configStore = useConfigStore()
+const equipType = configStore.getEquipType()
+
+const businessConfig = ref({
+  businessType: 'FWD',
+  title: '服务单/受理单编辑',
+  disableNavigate: true, // 是否禁用跳转,默认不禁用
+
+  ui: {
+    title: '服务单/受理单',
+    saveButtonText: '保存',
+    cancelButtonText: '取消',
+    showAdditionalToolbar: true,
+    customButtons: [],
+  },
+})
+const templateBlob = ref<string>('')
+const templateData = ref<any>({})
+
+let templateId: string = ''
+let refId: string = ''
+
+onLoad((options: any) => {
+  templateId = options.templateId
+  refId = options.refId
+  init()
+})
+
+const instId = ref<string>('')
+const init = async () => {
+  if (!templateId) {
+    return
+  }
+
+  const res = await getStandardTemplate({ id: templateId })
+  const resData = (res as any).data
+  const dataMap: any = {}
+
+  const dynamicTbValResp = await getDynamicTbVal({ refId })
+  const dynamicTb: any = dynamicTbValResp.data
+  instId.value = dynamicTb?.dynamicTbInsRespVO?.id || ''
+  for (let i = 0; i < dynamicTb.dynamicTbValRespVOList.length; i++) {
+    const item = dynamicTb.dynamicTbValRespVOList[i]
+    dataMap[item.colCode] = item.valValue
+  }
+
+  templateData.value = {
+    schema: resData.bindingPathSchema ? JSON.parse(resData.bindingPathSchema) : {},
+    data: {
+      ...dataMap,
+      templateId,
+      templateUrl: resData.fileUrl,
+    },
+    pathNameMapping: JSON.parse(resData.bindingPathNameJson),
+    template: templateId,
+    templateUrl: resData.fileUrl,
+  }
+
+  const fileUri = resData.fileUrl
+  const fileUrl = buildFileUrl(fileUri)
+  const fileBase64 = await downloadFileAsBase64(fileUrl)
+  templateBlob.value = fileBase64
+}
+
+const downloadFileAsBase64 = (fileUrl: string): Promise<string> => {
+  return new Promise((resolve, reject) => {
+    uni.request({
+      url: fileUrl,
+      method: 'GET',
+      responseType: 'arraybuffer',
+      success: (res) => {
+        if (res.statusCode === 200) {
+          const arrayBuffer = res.data as ArrayBuffer
+          const uint8Array = new Uint8Array(arrayBuffer)
+          const binaryString = uint8Array.reduce((data, byte) => {
+            return data + String.fromCharCode(byte)
+          }, '')
+          const base64 = btoa(binaryString)
+          resolve(base64) // 成功时 resolve Base64 字符串
+        } else {
+          reject(new Error(`Request failed with status ${res.statusCode}`))
+        }
+      },
+      fail: (err) => {
+        reject(err) // 失败时 reject 错误
+      },
+    })
+  })
+}
+
+const handleSave = async (data: any) => {
+  const result = await saveDynamicTbVal({ params: data.dataJSON, instId: instId.value })
+  if (result?.code === 0 && result?.data) {
+    uni.showToast({ title: '保存成功', icon: 'success' })
+  } else {
+    const msg = result?.msg || '保存失败'
+    uni.showToast({ title: msg, icon: 'error' })
+  }
+}
+
+const handleCancel = () => {
+  uni.navigateBack()
+}
+</script>

+ 101 - 54
src/pages/sign/index.vue

@@ -25,6 +25,7 @@
         :businessConfig="businessConfig"
         :templateData="templateData"
         :templateBlob="templateBlob"
+        viewer-height="50vh"
       />
 
       <!-- 签名区域分割线 -->
@@ -101,6 +102,13 @@
       <button class="footer-btn confirm-btn" @click="signSubmit">{{ signButtonText }}</button>
     </view>
 
+    <!-- 底部操作面板 -->
+    <wd-action-sheet
+      v-model="showActionSheet"
+      :actions="actionSheetActions"
+      @select="handleActionSelect"
+    />
+
     <!-- 服务单接收人确认弹窗 -->
     <view v-if="showFwdPopup" class="popup-overlay" @click="closeFwdPopup">
       <view class="popup-content" @click.stop>
@@ -115,26 +123,26 @@
 
     <!-- 推送任务单弹窗 -->
     <view v-if="showInputPopup" class="popup-overlay" @click="closeInputPopup">
-      <view class="popup-content" @click.stop>
+      <view class="popup-content input-popup" @click.stop>
         <text class="popup-title">推送任务单</text>
-        <view class="form-item">
-          <text class="form-label">接收人名称:</text>
-          <input class="form-input" v-model="inputName" placeholder="请输入接收人名称" />
+        <view class="input-row">
+          <text class="row-label">接收人名称:</text>
+          <input class="row-input" v-model="inputName" placeholder="请输入接收人名称" />
         </view>
-        <view class="form-item">
-          <text class="form-label">接收人手机号:</text>
+        <view class="input-row">
+          <text class="row-label">接收人手机号:</text>
           <input
-            class="form-input"
+            class="row-input"
             v-model="inputPhone"
             type="number"
             maxlength="11"
             placeholder="请输入接收人手机号"
           />
         </view>
-        <view v-if="routeType !== 'ZXXX'" class="form-item">
-          <text class="form-label">电子邮箱:</text>
+        <view v-if="routeType !== 'ZXXX'" class="input-row">
+          <text class="row-label">电子邮箱:</text>
           <input
-            class="form-input"
+            class="row-input"
             v-model="inputEmail"
             type="text"
             placeholder="请输入电子邮箱(选填)"
@@ -159,13 +167,16 @@ import NavBar from '@/components/NavBar/NavBar.vue'
 import { useUserStore } from '@/store/user'
 import { useConfigStore } from '@/store/config'
 
-import { getGcConfig, getTaskOrderImg, pushTaskOrder, uploadSignImage } from '@/api/sign'
+import { getGcConfig, getTaskOrderImg, pushPressure2TaskOrder, uploadSignImage } from '@/api/sign'
 import { getDynamicTbVal } from '@/api/task'
 import { getStandardTemplate } from '@/api/index'
 import { getTaskOrderReport } from '@/api/orderReport'
 import { buildFileUrl } from '@/utils/index'
 import { SignFuncName, requestFunc } from '@/api/ApiRouter/sign'
 import { TaskOrderFuncName, requestFunc as taskOrderRequestFunc } from '@/api/ApiRouter/taskOrder'
+import { EquipmentType } from '@/utils/dictMap'
+
+
 
 const equipType = useConfigStore().getEquipType()
 const title = ref('')
@@ -178,6 +189,7 @@ const unitContact = ref('')
 const unitPhone = ref('')
 const receiverEmail = ref('')
 const templateId = ref('')
+const refId = ref('')
 
 const showSignImg = ref('')
 const signImg = ref('')
@@ -188,6 +200,8 @@ const fwdInputPhone = ref('')
 
 const showFwdPopup = ref(false)
 const showInputPopup = ref(false)
+const showActionSheet = ref(false)
+const actionSheetActions = ref<any[]>([])
 const canvasWidth = ref(300)
 const inputName = ref('')
 const inputPhone = ref('')
@@ -341,6 +355,7 @@ onLoad((options) => {
 //   }
 // }
 
+const orderReportId = ref('')
 const getPreviewData = async () => {
   // orderId --> templateId + refId --> templateBlob 和 templateData
   /* 
@@ -348,8 +363,6 @@ const getPreviewData = async () => {
     templateId: dce2478ef6a153fbd2874b1f2ea33389
   */
   // orderId 查询服务单,获取 templateId + refId
-  let templateId = ''
-  let refId = ''
   if (!routeType.value) {
     uni.showToast({ title: '必须选择签字文件类型', icon: 'error' })
     return
@@ -362,18 +375,19 @@ const getPreviewData = async () => {
       if (orderReportRespList?.length) {
         orderReport = orderReportRespList[0]
       }
-      templateId = orderReport?.templateId
-      refId = orderReport?.acceptOrderId
+      templateId.value = orderReport?.templateId
+      refId.value = orderReport?.acceptOrderId
+      orderReportId.value = orderReport?.id || ''
       break
     case 'JYRS':
-      const orderDetail = await taskOrderRequestFunc(TaskOrderFuncName.TaskOrderDetail, equipType, { id: orderId.value })
+      const orderDetail = await taskOrderRequestFunc(TaskOrderFuncName.TaskOrderDetail, equipType, {
+        id: orderId.value,
+      })
       const notificationformReport = orderDetail.data?.notificationformReport
       if (!notificationformReport) {
         uni.showToast({ title: '暂无检验结果告知报表', icon: 'error' })
         return
       }
-      templateId = notificationformReport?.templateId
-      refId = notificationformReport?.id
       break
     default:
       uni.showToast({ title: '请选择正确的签字文件类型', icon: 'error' })
@@ -381,12 +395,12 @@ const getPreviewData = async () => {
   }
 
   // 获取templateSchema
-  const res = await getStandardTemplate({ id: templateId })
+  const res = await getStandardTemplate({ id: templateId.value })
   const resData = (res as any).data
   // 加载报表数据
   const dataMap: any = {}
   const dynamicTbResp = await getDynamicTbVal({
-    refId,
+    refId: refId.value,
   })
   const dynamicTb: any = dynamicTbResp.data
   for (let i = 0; i < dynamicTb.dynamicTbValRespVOList.length; i++) {
@@ -398,11 +412,11 @@ const getPreviewData = async () => {
     schema: resData.bindingPathSchema ? JSON.parse(resData.bindingPathSchema) : {},
     data: {
       ...dataMap,
-      templateId,
+      templateId: templateId.value,
       templateUrl: resData.fileUrl,
     },
     pathNameMapping: JSON.parse(resData.bindingPathNameJson),
-    template: templateId,
+    template: templateId.value,
     templateUrl: resData.fileUrl,
   }
 
@@ -604,6 +618,7 @@ const submitConfirm = async () => {
 
     if (routeType.value === 'FWD') {
       params.receiverPhone = fwdInputPhone.value
+      params.orderReportId = orderReportId.value
     }
 
     const result: any = await requestFunc(SignFuncName.SubmitSign, equipType, params)
@@ -642,38 +657,32 @@ const closeFwdPopup = () => {
 // 更多操作
 const handlePushOrder = () => {
   if (routeType.value === 'ZXXX') {
-    uni.showActionSheet({
-      itemList: ['小程序推送签名', '更新'],
-      success: (res) => {
-        if (res.tapIndex === 0) {
-          showInputPopup.value = true
-        } else if (res.tapIndex === 1) {
-          handleSpreadsheetEdit()
-        }
-      },
-    })
+    actionSheetActions.value = [{ name: '小程序推送签名' }, { name: '更新' }]
   } else {
-    uni.showActionSheet({
-      itemList: ['推送', '更新'],
-      success: (res) => {
-        if (res.tapIndex === 0) {
-          showInputPopup.value = true
-        } else if (res.tapIndex === 1) {
-          handleSpreadsheetEdit()
-        }
-      },
-    })
+    actionSheetActions.value = [{ name: '推送' }, { name: '更新' }]
   }
+  showActionSheet.value = true
 }
 
-// 跳转到电子表格编辑器
-const handleSpreadsheetEdit = () => {
-  if (!routeType.value || !orderId.value || !gcConfig.value) {
-    return uni.showToast({ title: '配置信息不完整', icon: 'none' })
+// 处理操作面板选择
+const handleActionSelect = (item: any) => {
+  const name = item.item.name
+  if (name === '推送' || name === '小程序推送签名') {
+    showInputPopup.value = true
+  } else if (name === '更新') {
+    if (!templateId.value || !refId.value) {
+      return uni.showToast({ title: '配置信息不完整', icon: 'none' })
+    }
+    let url = ''
+    if (routeType.value === 'FWD') {
+      url = `/pages/serviceOrderDetail/serviceOrderEditor?templateId=${templateId.value}&refId=${refId.value}`
+    } else if (routeType.value === 'JYRS') {
+      uni.showToast({ title: '更新检验结果告知表单暂未实现', icon: 'none' })
+    } else {
+      uni.showToast({ title: '系统错误,不能更新', icon: 'none' })
+    }
+    uni.navigateTo({ url })
   }
-
-  const url = `/pages/webview/generic-webview?businessType=${routeType.value}&orderId=${orderId.value}&templateId=${gcConfig.value.templateId || ''}&useOnline=1`
-  uni.navigateTo({ url })
 }
 
 // 关闭推送弹窗
@@ -693,16 +702,17 @@ const handlePushOrderSubmit = () => {
   }
 
   const params: any = {
-    id: orderId.value,
+    orderId: orderId.value,
     receiver: inputName.value,
     receiverPhone: inputPhone.value,
     receiverEmail: routeType.value !== 'ZXXX' ? inputEmail.value || '' : '',
     businessType: routeType.value ? businessTypeMap[routeType.value] : '',
     orderItemId: orderItemId.value || undefined,
     securityCheckId: securityCheckId.value,
+    equipMainType: equipTypeCode(equipType),
   }
 
-  pushTaskOrder(params)
+  pushPressure2TaskOrder(params)
     .then((res: any) => {
       if (res?.code === 0) {
         uni.showToast({ title: '推送成功', icon: 'success' })
@@ -718,9 +728,17 @@ const handlePushOrderSubmit = () => {
     })
 }
 
-// 返回
-const goBack = () => {
-  uni.navigateBack()
+const equipTypeCode = (type: EquipmentType) => {
+  switch (type) {
+    case EquipmentType.BOILER:
+      return '200'
+    case EquipmentType.PIPE:
+      return '300'
+    case EquipmentType.CONTAINER:
+      return '100'
+    default:
+      return ''
+  }
 }
 
 onMounted(() => {
@@ -982,6 +1000,11 @@ onMounted(() => {
     background-color: #fff;
     border-radius: 10px;
 
+    &.input-popup {
+      width: 80%;
+      max-width: 360px;
+    }
+
     .popup-title {
       margin-bottom: 15px;
       font-size: 18px;
@@ -1024,4 +1047,28 @@ onMounted(() => {
     }
   }
 }
+
+.input-row {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  width: 100%;
+  margin-bottom: 12px;
+
+  .row-label {
+    flex-shrink: 0;
+    width: 90px;
+    font-size: 14px;
+    color: #666;
+  }
+
+  .row-input {
+    flex: 1;
+    height: 36px;
+    padding: 0 10px;
+    font-size: 14px;
+    border: 1px solid #ddd;
+    border-radius: 5px;
+  }
+}
 </style>

+ 7 - 7
src/pages/taskOnline/TaskOnlineEquipmentList.vue

@@ -353,9 +353,9 @@ onLoad((options: any) => {
   fetchCheckerOnlineEquipmentList()
 })
 
-onShow(() => {
-  fetchCheckerOnlineEquipmentList()
-})
+// onShow(() => {
+//   fetchCheckerOnlineEquipmentList()
+// })
 
 const selectedEquipIds = computed(() => selectedEquipments.value.map((item) => item.equipId))
 
@@ -365,10 +365,10 @@ const fetchCheckerOnlineEquipmentList = async () => {
     pageSize: 999,
     pageNo: 1,
     taskStatusList: [400, 500, 510],
-    checkUserStrIds: userInfo.value?.id,
-    planCheckUserIds: userInfo.value?.id,
-    managerStrIds: userInfo.value?.id,
-    mainCheckerIds: userInfo.value?.id,
+    // checkUserStrIds: userInfo.value?.id,
+    // planCheckUserIds: userInfo.value?.id,
+    // managerStrIds: userInfo.value?.id,
+    // mainCheckerIds: userInfo.value?.id,
   }
   try {
     const equipmentListData = await requestFunc(

+ 2 - 0
src/pages/unClaim/components/TaskItem.vue

@@ -260,6 +260,7 @@ const handlePdfDetail = (businessType: number, signFilePdf: string) => {
   height: 29px;
   padding: 0 5px;
   margin-left: 3px;
+  margin-right: 1%;
   font-size: 11px;
   line-height: 1.4;
   border: none;
@@ -285,6 +286,7 @@ const handlePdfDetail = (businessType: number, signFilePdf: string) => {
   align-items: center;
   justify-content: center;
   min-width: 82px;
+  margin: auto 1%;
   height: 30px;
   font-size: 12px;
   border: none;

+ 10 - 1
src/pages/unClaim/components/UpdateContactPopup.vue

@@ -81,6 +81,8 @@
 <script lang="ts" setup>
 import { ref, reactive, watch } from 'vue'
 import { updateTaskOrder } from '@/api/task'
+import { useConfigStore } from '@/store/config'
+import { TaskOrderFuncName, requestFunc } from '@/api/ApiRouter/taskOrder'
 
 interface Props {
   popupData?: {
@@ -101,6 +103,10 @@ const emit = defineEmits<{
   refresh: []
 }>()
 
+const configStore = useConfigStore()
+
+const equipType = configStore.getEquipType()
+
 const tabs = [
   { value: '联系人', id: 'aaa' },
   { value: '电子报告接收人', id: 'bbb' },
@@ -173,7 +179,10 @@ const handleConfirm = async () => {
   }
 
   try {
-    const res = await updateTaskOrder({ ...cleanedData, id: formData.id })
+    const res = await requestFunc(TaskOrderFuncName.UpdateTaskOrder, equipType, {
+      ...cleanedData,
+      id: formData.id,
+    })
     if (res?.code === 0) {
       uni.showToast({ title: '修改成功', icon: 'success' })
       emit('hide')

+ 1 - 0
src/types/uni-pages.d.ts

@@ -12,6 +12,7 @@ interface NavigateToOptions {
        "/pages/securityCheck/securityCheckEditor" |
        "/pages/securityCheck/securityCheckList" |
        "/pages/serviceOrderDetail/index" |
+       "/pages/serviceOrderDetail/serviceOrderEditor" |
        "/pages/sign/index" |
        "/pages/sign-detail/index" |
        "/pages/systemFile/systemFile" |