Jelajahi Sumber

fix: 二轮测试修复

wzl 1 tahun lalu
induk
melakukan
9940eddaf4

+ 4 - 2
packages/core/src/index.js

@@ -2873,14 +2873,16 @@ class ComPDFKitViewer {
 
       const blobData = await this.messageHandler.sendWithPromise('SaveDocumentByStream', {
         doc,
-        saveType: 2
+        saveType: 2,
+        password: this.#pwd,
+        oldPassword: this.#oldPwd
       })
       const filename = this._docName
 
       if (op === 'saveAs') return { blobData, filename }
 
       const newUrl = URL.createObjectURL(blobData)
-      await this.loadDocument(newUrl, { filename })
+      await this.loadDocument(newUrl, { filename, notUpdatePwd: true })
       await clearDocument()
       return newUrl
     }

+ 33 - 6
packages/webview/src/components/Dialogs/ExtractPageSettingDialog.vue

@@ -11,7 +11,11 @@
         <div class="select-content">
           <div @click="pageRange = 'all'" class="option"><RadioBtnSel v-if="pageRange === 'all'" /><RadioBtnDis v-else />{{ $t('documentEditor.dialog.allPages') }}</div>
           <div @click="pageRange = 'odd'" class="option"><RadioBtnSel v-if="pageRange === 'odd'" /><RadioBtnDis v-else />{{ $t('documentEditor.dialog.oddPage') }}</div>
-          <div @click="pageRange = 'even'" class="option"><RadioBtnSel v-if="pageRange === 'even'" /><RadioBtnDis v-else />{{ $t('documentEditor.dialog.evenPage') }}</div>
+          <div @click="pageRange = 'even'" class="option" :class="{ 'disabled': totalPages === 1 }">
+            <div v-if="totalPages === 1" class="disabled-btn"></div>
+            <RadioBtnSel v-else-if="pageRange === 'even'" />
+            <RadioBtnDis v-else />
+            {{ $t('documentEditor.dialog.evenPage') }}</div>
           <div @click="pageRange = 'custom'" class="option custom-page">
             <RadioBtnSel v-if="pageRange === 'custom'" /><RadioBtnDis v-else />{{ $t('documentEditor.dialog.page') }}
             <div class="addition">
@@ -27,8 +31,8 @@
           <div class="check" :class="{'active': separateFile}"><Checkbox v-show="separateFile" /></div>
           <span>{{ $t('documentEditor.eachPage') }}</span>
         </div>
-        <div class="check-box" :class="{ 'disabled': pageRange === 'all' }" @click="() => { if (pageRange !== 'all') deleteAfter = !deleteAfter }">
-          <div class="check" :class="{'active': deleteAfter}"><Checkbox v-show="deleteAfter && pageRange !== 'all'" /></div>
+        <div class="check-box" :class="{ 'disabled': checkDeleteAfter }" @click="() => { if (!checkDeleteAfter) deleteAfter = !deleteAfter }">
+          <div class="check" :class="{'active': deleteAfter}"><Checkbox v-show="deleteAfter && !checkDeleteAfter" /></div>
           <span>{{ $t('documentEditor.deleteAfter') }}</span>
         </div>
       </div>
@@ -110,7 +114,7 @@ const formatText = (inputText) => {
             String(Number(smallest) + i)
           )
         } else {
-          return Math.min(match, max)
+          return Math.min(Math.max(match, 1), max)
         }
       })
       .sort((a, b) => a - b)
@@ -140,6 +144,12 @@ const formatText = (inputText) => {
 const formatRange = (start, end) => {
   return (start === end) ? start.toString() : `${start}-${end}`
 }
+
+const checkDeleteAfter = computed(() => {
+  if (pageRange.value === 'all') return true
+  if (props.totalPages === 1 && pageRange.value !== 'even') return true
+  if (props.totalPages > 1 && pageRange.value === 'custom' && customRange.value === '1-' + props.totalPages) return true
+})
 </script>
 
 <style lang="scss">
@@ -166,8 +176,25 @@ const formatRange = (start, end) => {
     border-radius: 4px;
     background: var(--c-toolbar-bg);
 
-    .select-content .option:not(:first-child) {
-      margin-top: 16px;
+    .select-content {
+
+      .option:not(:first-child) {
+        margin-top: 16px;
+      }
+
+      .option.disabled {
+        pointer-events: none;
+        opacity: 0.5;
+
+        .disabled-btn {
+          margin-right: 4px;
+          width: 12px;
+          height: 12px;
+          border-radius: 50%;
+          border: 1px solid #666;
+          background-color: #b4b4b4;
+        }
+      }
     }
     
     .addition {

+ 114 - 42
packages/webview/src/components/DocumentEditorContainer/DocumentEditorContainer.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="document-editor-container">
+  <div class="document-editor-container" ref="docEditorContainer">
     <!-- 编辑工具 -->
     <div class="tools-container">
       <Button class="with-text" @click="openInsertPageDialog">
@@ -48,20 +48,17 @@
           'drag-indicate-right': dragToIndex === index && !isDragToLeft,
           'drag-indicate-left': dragToIndex + 1 === index && isDragToLeft,
           'drag-indicate-disable': dragFromIndex >= 0 && dragToIndex >= -1 && dragIndicateDisable(index),
-          'has-hover': dragFromIndex < 0 && !selectedPageList.includes(index)
+          'has-hover': !isMobileDevice && dragFromIndex < 0 && !selectedPageList.includes(index)
         }"
         ref="imgBoxEl"
       >
         <div class="img-box">
           <img v-if="item.img" :src="item.img" @click="selectPage(index)" :style="`transform: rotate(${rotationToAngle(item.rotation)}deg);`" />
-          <img v-else-if="item.type === 'blank'"
-            :class="{ 'blank-page': item.type === 'blank', 'w-auto': item.size.width <= item.size.height, 'h-auto': item.size.width > item.size.height }"
-            :width="item.size.width / ratio"
-            :height="item.size.height / ratio"
+          <div v-else-if="item.type === 'blank'"
+            :class="{ 'blank-page': item.type === 'blank' }"
             @click="selectPage(index)"
-            :style="`transform: rotate(${rotationToAngle(item.rotation)}deg);`"
-          />
-          <!-- <div v-else-if="item.type === 'pdf'" class="pdf-page" @click="selectPage(index)"><DocumentPage /></div> -->
+            :style="{ 'transform': 'rotate(' + rotationToAngle(item.rotation) + 'deg)', 'width': blankPageScaleSize(item.size).width + 'px', 'height': blankPageScaleSize(item.size).height + 'px' }">
+          </div>
         </div>
         <p>{{ index + 1 }}</p>
       </div>
@@ -75,7 +72,7 @@
 </template>
 
 <script setup>
-import { ref, onMounted, reactive, onUnmounted } from 'vue'
+import { ref, onMounted, reactive, onUnmounted, computed } from 'vue'
 import { useViewerStore } from '@/stores/modules/viewer'
 import { useDocumentStore } from '@/stores/modules/document'
 import core from '@/core'
@@ -96,9 +93,13 @@ const dragFromIndex = ref(-2)
 const dragToIndex = ref(-2)
 const isDragToLeft = ref(false)
 const imgBoxEl = ref(null)
+const marginLeft = ref(0)
+const docEditorContainer = ref(null)
 
 const ratio = window.devicePixelRatio || 1
-const marginLeft = ref(0)
+const isMobileWidth = window.innerWidth < 768
+const isMobileDevice = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
+let timer = null
 
 onMounted(() => {
   Sortable.create(dragContainer.value, {
@@ -110,7 +111,8 @@ onMounted(() => {
     forceFallback: true,
     fallbackTolerance: 20,
     onStart: dragStart,
-    onEnd: dragEnd
+    onEnd: dragEnd,
+    delay: isMobileDevice ? 200 : 0
   })
 
   const { width, height } = imgBoxEl.value[0].getBoundingClientRect()
@@ -196,6 +198,30 @@ const handleInsertPage = async (data) => {
   selectedPageList.length = 0
 }
 
+// 计算空白页的显示大小
+const blankPageScaleSize = (size) => {
+  const { width, height } = size
+  const boxWidth = isMobileWidth ? 140 : 204
+  const boxHeight = isMobileWidth ? 141 : 216
+
+  const scaleWidth = width / ratio
+  const scaleHeight = height / ratio
+
+  const widthRatio = boxWidth / scaleWidth
+  const heightRatio = boxHeight / scaleHeight
+
+  let scaleRatio = Math.min(widthRatio, heightRatio)
+
+  if (scaleRatio > 1) {
+    scaleRatio = 1
+  }
+
+  const scaledWidth = scaleWidth * scaleRatio
+  const scaledHeight = scaleHeight * scaleRatio
+
+  return { width: scaledWidth, height: scaledHeight}
+}
+
 // 选中页面
 const selectPage = (index) => {
   const i = selectedPageList.indexOf(index)
@@ -326,7 +352,7 @@ const handleExtractPage = async (data) => {
       for (let i = start; i <= end; i++) {
         indexArray.push(i - 1)
       }
-    } else {
+    } else if (part) {
       indexArray.push(parseInt(part) - 1)
     }
   })
@@ -427,7 +453,14 @@ const handleMovePage = (targetIndex) => {
 // 拖拽开始
 const dragStart = (e) => {
   dragFromIndex.value = e.oldIndex
-  dragContainer.value.addEventListener('mousemove', dragging)
+  dragContainer.value.addEventListener(isMobileDevice ? 'touchmove' : 'mousemove', dragging)
+
+  if (isMobileDevice) {
+    document.body.style.overscrollBehavior = 'none'
+    document.body.style.userSelect = 'none'
+    document.getElementById('app').style.touchAction = 'none'
+    docEditorContainer.value.style.overflow = 'hidden'
+  }
 }
 
 // 拖拽中
@@ -436,14 +469,17 @@ const dragging = (e) => {
     dragContainer.value.querySelector('.sortable-drag').style.setProperty("--after-content", "'" + selectedPageList.length + "'")
   }
 
-  let target = e.target
+  let target = isMobileDevice ? document.elementFromPoint(e.touches[0].clientX, e.touches[0].clientY) : e.target
   const pageDomList = dragContainer.value.children
 
   for (let i = 0; i < pageDomList.length; i++) {
     const element = pageDomList[i]
+
     if (element.contains(target)) {
-      const domRect = element.getBoundingClientRect().width
-      isDragToLeft.value = e.offsetX < domRect / 2
+      const domRect = element.getBoundingClientRect()
+      const offsetX = isMobileDevice ? (e.touches[0].clientX - domRect.left) : (e.offsetX + 30)
+      isDragToLeft.value = offsetX < domRect.width / 2
+
       if (isDragToLeft.value) {
         dragToIndex.value = i - 1
       } else {
@@ -451,16 +487,25 @@ const dragging = (e) => {
       }
     }
   }
+
+  if (isMobileDevice) handleAutoScroll(e.touches[0].clientY)
 }
 
 // 拖拽结束
 const dragEnd = () => {
-  dragContainer.value.removeEventListener('mousemove', dragging)
-  if (!dragIndicateDisable(dragToIndex.value)) {
+  dragContainer.value.removeEventListener(isMobileDevice ? 'touchmove' : 'mousemove', dragging)
+  if (dragToIndex.value > -2 && !dragIndicateDisable(dragToIndex.value)) {
     handleMovePage(dragToIndex.value + 1)
   }
   dragFromIndex.value = -2
   dragToIndex.value = -2
+
+  if (isMobileDevice) {
+    document.body.style.overscrollBehavior = 'auto'
+    document.body.style.userSelect = 'auto'
+    document.getElementById('app').style.touchAction = 'auto'
+    docEditorContainer.value.style.overflow = 'auto'
+  }
 }
 
 // 拖拽计算显示位置的竖条是否置灰
@@ -495,8 +540,42 @@ const isConsecutive = (arr) => {
 // 计算左边距
 const updateMarginLeft = () => {
   const width = window.innerWidth
-  const pageWidth = width > 567 ? 280 : 170
-  marginLeft.value = width % pageWidth / 2
+  const pageWidth = width > 767 ? 280 : 170
+  marginLeft.value = parseInt(width % pageWidth / 2)
+}
+
+// 移动端处理自动滚动
+const handleAutoScroll = (clientY) => {
+  const threshold = 100
+  const containerEl = docEditorContainer.value
+  const containerRect = containerEl.getBoundingClientRect()
+  const containerHeight = containerRect.height
+  const deltaY = clientY
+  const totalHeight = containerEl.scrollHeight
+  
+  // 判断鼠标位置是否在阈值范围内
+  if (deltaY <= threshold || deltaY >= containerHeight - threshold) {
+    clearInterval(timer)
+    timer = setInterval(() => {
+      // 向上滚动
+      if (deltaY <= threshold) {
+        containerEl.scrollTop -= 10
+        if (containerEl.scrollTop <= 0) {
+          clearInterval(timer)
+        }
+      }
+      // 向下滚动
+      else if (deltaY >= containerHeight - threshold) {
+        containerEl.scrollTop += 10
+        if (containerEl.scrollTop + containerHeight >= totalHeight) {
+          clearInterval(timer)
+        }
+      }
+    }, 10)
+  } else {
+    // 鼠标位置不在阈值范围内,停止滚动
+    clearInterval(timer)
+  }
 }
 </script>
 
@@ -562,31 +641,16 @@ const updateMarginLeft = () => {
         height: calc(100% - 24px);
 
         img,
-        .pdf-page {
+        .blank-page {
           max-width: 100%;
           max-height: 100%;
           border: 0.5px solid rgba(0, 0, 0, 0.12);
+          -webkit-touch-callout: none;
+          -ms-touch-action: manipulation;
+          touch-action: manipulation; 
         }
 
-        img.blank-page {
-          content-visibility: hidden;
-          background-color: #fff;
-
-          &.w-auto {
-            width: auto;
-          }
-
-          &.h-auto {
-            height: auto;
-          }
-        }
-
-        .pdf-page {
-          display: flex;
-          align-items: center;
-          justify-content: center;
-          width: 168px;
-          height: 216px;
+        .blank-page {
           background-color: #fff;
         }
       }
@@ -674,13 +738,21 @@ const updateMarginLeft = () => {
   }
 }
 
-@media screen and (max-width: 567px) {
+@media screen and (max-width: 767px) {
   .document-editor-container .page-container {
     .page {
       margin: 12px 10px;
       padding: 5px;
       width: 150px;
       height: 175px;
+
+      &.drag-indicate-right:after {
+        right: -12px;
+      }
+
+      &.drag-indicate-left::before {
+        left: -12px;
+      }
     }
   }
 }

+ 4 - 0
packages/webview/src/components/DocumentEditorHeader/DocumentEditorHeader.vue

@@ -21,6 +21,8 @@ const useDocument = useDocumentStore()
 
 const docEditorOperationList = computed(() => useDocument.getDocEditorOperationList)
 const toolMode = computed(() => useViewer.getToolMode)
+const scrollMode = computed(() => useViewer.getScrollMode)
+const pageMode = computed(() => useViewer.getPageMode)
 
 watch(toolMode, (newToolMode, oldToolMode) => {
   if (oldToolMode === 'document') {
@@ -49,6 +51,8 @@ const save = async () => {
   useDocument.setTotalPages(totalPages)
   useDocument.setOutline(core.getOutlines())
   useViewer.setCurrentPage(1)
+  core.switchScrollMode(scrollMode.value === 'Vertical' ? 0 : 1)
+  core.switchSpreadMode(pageMode.value)
 
   useViewer.setUploadLoading(false)
   useViewer.setUpload(true)