|
@@ -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;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|