hookehuyr

feat(PdfViewer): 添加页码跳转功能

实现页码显示和跳转功能,包括当前页/总页数显示和输入框跳转
处理PDF初始化时的总页数获取和页码边界检查
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
21 :scrollThreshold="300" 21 :scrollThreshold="300"
22 :pdfWidth="`${Math.round(100 * zoomLevel)}%`" 22 :pdfWidth="`${Math.round(100 * zoomLevel)}%`"
23 :rowGap="8" 23 :rowGap="8"
24 - :page="1" 24 + :page="currentPage"
25 :cMapUrl="'https://unpkg.com/pdfjs-dist@3.7.107/cmaps/'" 25 :cMapUrl="'https://unpkg.com/pdfjs-dist@3.7.107/cmaps/'"
26 :withCredentials="false" 26 :withCredentials="false"
27 :useSystemFonts="true" 27 :useSystemFonts="true"
...@@ -56,7 +56,20 @@ ...@@ -56,7 +56,20 @@
56 <div v-if="!loading && !loadingError" class="zoom-controls"> 56 <div v-if="!loading && !loadingError" class="zoom-controls">
57 <van-button class="zoom-btn zoom-close-btn" type="default" icon="cross" round @click="handleClose" /> 57 <van-button class="zoom-btn zoom-close-btn" type="default" icon="cross" round @click="handleClose" />
58 <van-button class="zoom-btn zoom-in-btn" type="default" icon="plus" round @click="zoomIn" /> 58 <van-button class="zoom-btn zoom-in-btn" type="default" icon="plus" round @click="zoomIn" />
59 - <div class="zoom-level">{{ Math.round(zoomLevel * 100) }}%</div> 59 + <div class="page-jump" @click="focusPageInput">
60 + <span class="page-display">{{ currentPage }}/{{ totalPages || 0 }}</span>
61 + <input
62 + ref="pageInputRef"
63 + class="page-input"
64 + type="number"
65 + :min="1"
66 + :max="totalPages || 1"
67 + v-model="pageInput"
68 + @blur="handlePageInputBlur"
69 + @keyup.enter="handlePageInputBlur"
70 + v-show="isEditingPage"
71 + />
72 + </div>
60 <van-button class="zoom-btn zoom-out-btn" type="default" icon="minus" round @click="zoomOut" /> 73 <van-button class="zoom-btn zoom-out-btn" type="default" icon="minus" round @click="zoomOut" />
61 <van-button class="zoom-btn zoom-reset-btn" type="default" round @click="resetZoom"> 74 <van-button class="zoom-btn zoom-reset-btn" type="default" round @click="resetZoom">
62 <van-icon name="replay" size="16" /> 75 <van-icon name="replay" size="16" />
...@@ -130,6 +143,42 @@ const zoomLevel = ref(1); // 缩放级别,1为100% ...@@ -130,6 +143,42 @@ const zoomLevel = ref(1); // 缩放级别,1为100%
130 const scrollPosition = ref({ x: 0, y: 0 }); // 记录滚动位置 143 const scrollPosition = ref({ x: 0, y: 0 }); // 记录滚动位置
131 const pdfRef = ref(null); // PDF组件引用 144 const pdfRef = ref(null); // PDF组件引用
132 145
146 +// 页码控制
147 +const currentPage = ref(1);
148 +const totalPages = ref(0);
149 +const pageInput = ref('');
150 +const isEditingPage = ref(false);
151 +const pageInputRef = ref(null);
152 +
153 +/**
154 + * 处理页码输入框失焦事件
155 + */
156 +const handlePageInputBlur = () => {
157 + const inputValue = parseInt(pageInput.value);
158 + if (!isNaN(inputValue)) {
159 + const validPage = Math.max(1, Math.min(inputValue, totalPages.value || 1));
160 + currentPage.value = validPage;
161 + }
162 + pageInput.value = '';
163 + isEditingPage.value = false;
164 +};
165 +
166 +/**
167 + * 点击页码显示区域,切换到编辑模式
168 + */
169 +const focusPageInput = () => {
170 + if (!isEditingPage.value) {
171 + isEditingPage.value = true;
172 + pageInput.value = currentPage.value.toString();
173 + nextTick(() => {
174 + if (pageInputRef.value) {
175 + pageInputRef.value.focus();
176 + pageInputRef.value.select();
177 + }
178 + });
179 + }
180 +};
181 +
133 // 超时处理相关 182 // 超时处理相关
134 const loadingTimeout = ref(30000); // 超时时间,默认30秒 183 const loadingTimeout = ref(30000); // 超时时间,默认30秒
135 const timeoutTimer = ref(null); // 超时计时器 184 const timeoutTimer = ref(null); // 超时计时器
...@@ -270,7 +319,8 @@ const handleScroll = (scrollOffset) => { ...@@ -270,7 +319,8 @@ const handleScroll = (scrollOffset) => {
270 * @param {number} page - 当前页码 319 * @param {number} page - 当前页码
271 */ 320 */
272 const handlePageChange = (page) => { 321 const handlePageChange = (page) => {
273 - // 可以在这里处理页面变化事件 322 + const p = Number(page) || 1;
323 + currentPage.value = p;
274 }; 324 };
275 325
276 /** 326 /**
...@@ -283,6 +333,18 @@ const handlePdfInit = (pdf) => { ...@@ -283,6 +333,18 @@ const handlePdfInit = (pdf) => {
283 333
284 // PDF初始化完成后的处理 334 // PDF初始化完成后的处理
285 loadingError.value = false; 335 loadingError.value = false;
336 +
337 + // 记录总页数
338 + try {
339 + const num = (pdf && typeof pdf.numPages === 'number') ? pdf.numPages : 0;
340 + if (num > 0) {
341 + totalPages.value = num;
342 + if (currentPage.value > num) currentPage.value = num;
343 + }
344 + } catch (e) {
345 + // 忽略无法获取页数的情况
346 + }
347 +
286 nextTick(() => { 348 nextTick(() => {
287 if (props.preventSave) { 349 if (props.preventSave) {
288 // 添加防止保存的事件监听器 350 // 添加防止保存的事件监听器
...@@ -685,6 +747,59 @@ const addPreventSaveListeners = () => { ...@@ -685,6 +747,59 @@ const addPreventSaveListeners = () => {
685 line-height: 1.4; 747 line-height: 1.4;
686 } 748 }
687 749
750 +.page-jump {
751 + display: flex;
752 + align-items: center;
753 + justify-content: center;
754 + position: relative;
755 + cursor: pointer;
756 + min-width: 60px;
757 + height: 32px;
758 + background: rgba(0, 0, 0, 0.6);
759 + border-radius: 16px;
760 + padding: 0 12px;
761 +}
762 +
763 +.page-display {
764 + color: white;
765 + font-size: 14px;
766 + font-weight: 500;
767 + white-space: nowrap;
768 + user-select: none;
769 +}
770 +
771 +.page-input {
772 + position: absolute;
773 + top: 0;
774 + left: 0;
775 + right: 0;
776 + bottom: 0;
777 + width: 100%;
778 + height: 100%;
779 + background: rgba(255, 255, 255, 0.9);
780 + border: 2px solid #1989fa;
781 + border-radius: 16px;
782 + text-align: center;
783 + font-size: 14px;
784 + font-weight: 500;
785 + color: #333;
786 + outline: none;
787 +}
788 +
789 +.page-input::-webkit-outer-spin-button,
790 +.page-input::-webkit-inner-spin-button {
791 + -webkit-appearance: none;
792 + margin: 0;
793 +}
794 +
795 +.page-input[type=number] {
796 + -moz-appearance: textfield;
797 +}
798 +
799 +.page-jump:hover {
800 + background: rgba(0, 0, 0, 0.8);
801 +}
802 +
688 /* 错误状态样式 */ 803 /* 错误状态样式 */
689 .error-container { 804 .error-container {
690 display: flex; 805 display: flex;
......