From 5848878fb59d890dc7fbedb9463df46568c7ac85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rhett=E9=9C=8D?= <741354752@qq.com> Date: Fri, 13 Feb 2026 16:59:30 +0800 Subject: [PATCH] =?UTF-8?q?pdf=E9=A2=84=E8=A7=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/FileUpload/FileUpload.js | 3 +- config.js | 4 +- package.json | 1 + .../OrderManager/components/LookDataNB.vue | 1 - utils/request.js | 19 +- utils/utils.js | 685 +++++++++++++++++- 6 files changed, 698 insertions(+), 15 deletions(-) diff --git a/api/FileUpload/FileUpload.js b/api/FileUpload/FileUpload.js index 66fec34..79032c6 100644 --- a/api/FileUpload/FileUpload.js +++ b/api/FileUpload/FileUpload.js @@ -43,12 +43,13 @@ export function uploadSecurityFileByType(query,data) { } // 通用上传文件下载-通用文件下载 +// api/FileUpload/FileUpload.js export function securityFileDownload(query) { return request({ url: '/common/securityFileDownload', method: 'get', params: query, - responseType: 'blob', // 设置响应类型为 blob + responseType: 'arraybuffer', // 改为 arraybuffer isEncrypt: false }) } diff --git a/config.js b/config.js index d162a7b..3492141 100644 --- a/config.js +++ b/config.js @@ -2,8 +2,8 @@ export default { // baseUrl: 'https://vue.ruoyi.vip/prod-api', // // baseUrl: '/prod-api', //前后端分离版的接口地址,转发代理 设置在了manifest.json文件中 - baseUrl: 'http://192.168.99.25:18090', //--慎用--慎用--慎用前后端分离版的接口地址--大冢服务器--慎用 - // baseUrl: 'http://106.15.139.36:18090', //前后端分离版的接口地址 + // baseUrl: 'http://192.168.99.25:18090', //--慎用--慎用--慎用前后端分离版的接口地址--大冢服务器--慎用 + baseUrl: 'http://106.15.139.36:18090', //前后端分离版的接口地址 // baseUrl: '/prod-api', //前后端分离版的接口地址 //测试提交 diff --git a/package.json b/package.json index b06a9b5..608d15e 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "dependencies": { + "pdfjs-dist": "^2.16.105", "pinia-plugin-persistedstate": "^4.7.1", "radash": "^12.1.1" } diff --git a/pages/work/OrderManager/components/LookDataNB.vue b/pages/work/OrderManager/components/LookDataNB.vue index b6de62a..142d310 100644 --- a/pages/work/OrderManager/components/LookDataNB.vue +++ b/pages/work/OrderManager/components/LookDataNB.vue @@ -59,7 +59,6 @@ > {{ form.loadfile.split('\\').pop() }} - 审核表(OA): diff --git a/utils/request.js b/utils/request.js index 21d2a3f..5bf2165 100644 --- a/utils/request.js +++ b/utils/request.js @@ -20,18 +20,31 @@ const request = config => { url = url.slice(0, -1) config.url = url } + + // 关键:获取 responseType,默认为 'json' + const responseType = config.responseType || 'json' + return new Promise((resolve, reject) => { uni.request({ method: config.method || 'get', - timeout: config.timeout || timeout, + timeout: config.timeout || timeout, url: config.baseUrl || baseUrl + config.url, data: config.data, header: config.header, - dataType: 'json' + dataType: 'json', + responseType: responseType // 添加 responseType 支持 }).then(response => { + // 如果是 arraybuffer 或 blob 类型,直接返回原始数据 + if (responseType === 'arraybuffer' || responseType === 'blob') { + resolve(response.data) + return + } + + // 原有的 JSON 响应处理逻辑 const res = response const code = res.data.code || 200 const msg = errorCode[code] || res.data.msg || errorCode['default'] + if (code === 401) { showConfirm('登录状态已过期,您可以继续留在该页面,或者重新登录?').then(res => { if (res.confirm) { @@ -65,4 +78,4 @@ const request = config => { }) } -export default request +export default request \ No newline at end of file diff --git a/utils/utils.js b/utils/utils.js index b122206..32e319a 100644 --- a/utils/utils.js +++ b/utils/utils.js @@ -4,10 +4,598 @@ export const formatPrice = (value) => { return Number(value).toFixed(2) } - import { securityFileDownload } from '@/api/FileUpload/FileUpload.js'; +import * as pdfjsLib from 'pdfjs-dist'; -export const YuLanfile = async (filePath) => { +// 设置PDF.js worker路径(使用CDN) +pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.worker.min.js'; + +// 存储当前页面的弹框实例 +let popupInstance = null; +let styleInjected = false; + +// 注入CSS样式 +const injectPdfStyle = () => { + if (styleInjected) return; + + const styleId = 'pdf_preview_style'; + if (!document.getElementById(styleId)) { + const style = document.createElement('style'); + style.id = styleId; + style.innerHTML = ` + .pdf-preview-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 9999; + } + .pdf-preview-container { + width: 95vw; + max-width: 900px; + height: 90vh; + background-color: #fff; + border-radius: 12px; + display: flex; + flex-direction: column; + overflow: hidden; + animation: pdfFadeIn 0.3s; + } + @keyframes pdfFadeIn { + from { + opacity: 0; + transform: scale(0.9); + } + to { + opacity: 1; + transform: scale(1); + } + } + .pdf-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 12px 16px; + border-bottom: 1px solid #eee; + background-color: #fff; + } + .pdf-title { + font-size: 16px; + font-weight: bold; + color: #333; + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .pdf-close { + font-size: 28px; + color: #999; + cursor: pointer; + padding: 0 8px; + line-height: 1; + } + .pdf-close:hover { + color: #333; + } + .pdf-content { + flex: 1; + background-color: #525659; + position: relative; + overflow: auto; + padding: 20px; + box-sizing: border-box; + } + .pdf-canvas-container { + display: flex; + flex-direction: column; + align-items: center; + gap: 15px; + } + .pdf-page-canvas { + width: 100%; + height: auto; + box-shadow: 0 2px 10px rgba(0,0,0,0.1); + border-radius: 4px; + background-color: white; + } + .pdf-footer { + display: flex; + justify-content: flex-end; + padding: 12px 16px; + border-top: 1px solid #eee; + background-color: #fff; + } + .pdf-footer button { + margin: 0 0 0 8px; + min-width: 80px; + padding: 8px 16px; + border-radius: 4px; + border: 1px solid #dcdfe6; + background-color: #fff; + color: #606266; + cursor: pointer; + font-size: 14px; + } + .pdf-footer button:hover { + background-color: #f5f7fa; + } + .pdf-footer button.primary { + background-color: #409eff; + color: #fff; + border-color: #409eff; + } + .pdf-footer button.primary:hover { + background-color: #66b1ff; + } + .pdf-loading { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + color: #fff; + font-size: 16px; + text-align: center; + } + .pdf-pagination { + display: flex; + justify-content: center; + align-items: center; + padding: 12px; + background-color: #fff; + border-top: 1px solid #eee; + } + .pdf-pagination button { + margin: 0 8px; + padding: 4px 12px; + border: 1px solid #dcdfe6; + background-color: #fff; + border-radius: 4px; + cursor: pointer; + } + .pdf-pagination button:disabled { + opacity: 0.5; + cursor: not-allowed; + } + .pdf-pagination span { + margin: 0 8px; + color: #606266; + } + `; + document.head.appendChild(style); + styleInjected = true; + } +}; + +// H5环境:使用Canvas渲染PDF +const createH5Popup = (url, fileName) => { + // 先关闭已存在的弹框 + if (popupInstance) { + document.body.removeChild(popupInstance); + popupInstance = null; + } + + // 注入样式 + injectPdfStyle(); + + // 创建遮罩层 + const overlay = document.createElement('div'); + overlay.className = 'pdf-preview-overlay'; + + // 创建弹框容器 + const container = document.createElement('div'); + container.className = 'pdf-preview-container'; + + // 创建头部 + const header = document.createElement('div'); + header.className = 'pdf-header'; + + const title = document.createElement('span'); + title.className = 'pdf-title'; + title.textContent = fileName || 'PDF预览'; + + const closeBtn = document.createElement('span'); + closeBtn.className = 'pdf-close'; + closeBtn.textContent = '×'; + closeBtn.onclick = () => { + document.body.removeChild(overlay); + if (url && url.startsWith('blob:')) { + URL.revokeObjectURL(url); + } + popupInstance = null; + }; + + header.appendChild(title); + // header.appendChild(closeBtn); + + // 创建内容区域 + const content = document.createElement('div'); + content.className = 'pdf-content'; + + // 创建Canvas容器 + const canvasContainer = document.createElement('div'); + canvasContainer.className = 'pdf-canvas-container'; + content.appendChild(canvasContainer); + + // 添加加载提示 + const loadingDiv = document.createElement('div'); + loadingDiv.className = 'pdf-loading'; + loadingDiv.textContent = '正在加载PDF文档...'; + content.appendChild(loadingDiv); + + container.appendChild(header); + container.appendChild(content); + + // 创建底部 + const footer = document.createElement('div'); + footer.className = 'pdf-footer'; + + const closeFooterBtn = document.createElement('button'); + closeFooterBtn.textContent = '关闭'; + closeFooterBtn.onclick = closeBtn.onclick; + + const downloadBtn = document.createElement('button'); + downloadBtn.textContent = '下载'; + downloadBtn.className = 'primary'; + downloadBtn.onclick = () => { + window.open(url); + }; + + // footer.appendChild(downloadBtn); + footer.appendChild(closeFooterBtn); + container.appendChild(footer); + overlay.appendChild(container); + document.body.appendChild(overlay); + + // 使用PDF.js渲染PDF + pdfjsLib.getDocument(url).promise.then(pdf => { + // 移除加载提示 + if (content.contains(loadingDiv)) { + content.removeChild(loadingDiv); + } + + // 添加分页控制器 + const paginationDiv = document.createElement('div'); + paginationDiv.className = 'pdf-pagination'; + + let currentPage = 1; + const totalPages = pdf.numPages; + + const prevBtn = document.createElement('button'); + prevBtn.textContent = '上一页'; + prevBtn.disabled = true; + + const nextBtn = document.createElement('button'); + nextBtn.textContent = '下一页'; + + const pageInfo = document.createElement('span'); + pageInfo.textContent = `${currentPage} / ${totalPages}`; + + prevBtn.onclick = () => { + if (currentPage > 1) { + currentPage--; + renderPage(currentPage); + pageInfo.textContent = `${currentPage} / ${totalPages}`; + prevBtn.disabled = currentPage === 1; + nextBtn.disabled = currentPage === totalPages; + } + }; + + nextBtn.onclick = () => { + if (currentPage < totalPages) { + currentPage++; + renderPage(currentPage); + pageInfo.textContent = `${currentPage} / ${totalPages}`; + prevBtn.disabled = currentPage === 1; + nextBtn.disabled = currentPage === totalPages; + } + }; + + // paginationDiv.appendChild(prevBtn); + // paginationDiv.appendChild(pageInfo); + // paginationDiv.appendChild(nextBtn); + // content.appendChild(paginationDiv); + + // 创建Canvas元素 + const canvas = document.createElement('canvas'); + canvas.className = 'pdf-page-canvas'; + canvasContainer.appendChild(canvas); + + // 渲染指定页面 + const renderPage = (pageNumber) => { + pdf.getPage(pageNumber).then(page => { + // 根据容器宽度计算缩放比例 + const containerWidth = content.clientWidth - 40; // 减去padding + const viewport = page.getViewport({ scale: 1.0 }); + const scale = containerWidth / viewport.width; + + const scaledViewport = page.getViewport({ scale }); + + canvas.height = scaledViewport.height; + canvas.width = scaledViewport.width; + + const context = canvas.getContext('2d'); + + const renderContext = { + canvasContext: context, + viewport: scaledViewport + }; + + return page.render(renderContext).promise; + }); + }; + + // 渲染第一页 + renderPage(1); + + // 监听窗口大小变化,重新渲染 + const resizeHandler = () => { + renderPage(currentPage); + }; + window.addEventListener('resize', resizeHandler); + + // 清理事件监听 + const originalClose = closeBtn.onclick; + closeBtn.onclick = () => { + window.removeEventListener('resize', resizeHandler); + originalClose.call(closeBtn); + }; + + }).catch(error => { + console.error('PDF渲染失败:', error); + if (content.contains(loadingDiv)) { + content.removeChild(loadingDiv); + } + content.innerHTML = ` +
+
+ ⚠️ PDF预览失败 +
+
+ ${error.message || '未知错误'} +
+ +
+ `; + }); + + popupInstance = overlay; + + overlay.addEventListener('click', (e) => { + if (e.target === overlay) { + closeBtn.onclick(); + } + }); + + return overlay; +}; + +// APP环境:保存Blob到临时文件 +const saveBlobToFile = (blob, fileName) => { + return new Promise((resolve, reject) => { + // #ifdef APP-PLUS + const reader = new FileReader(); + reader.onload = (e) => { + const base64Data = e.target.result.split(',')[1]; + const filePath = '_doc/' + Date.now() + '_' + fileName; + plus.io.writeFile({ + filename: filePath, + data: base64Data, + success: () => { + resolve(filePath); + }, + fail: (err) => { + reject(err); + } + }); + }; + reader.onerror = (err) => { + reject(err); + }; + reader.readAsDataURL(blob); + // #endif + // #ifndef APP-PLUS + reject(new Error('非APP环境')); + // #endif + }); +}; + +// APP环境:使用本地文件预览 +const createAppPopup = (filePath, fileName) => { + // #ifdef APP-PLUS + if (popupInstance) { + popupInstance.close(); + popupInstance = null; + } + + const popupId = 'pdf_preview_' + Date.now(); + + const html = ` + + + + + + + + +
+ ${fileName || 'PDF预览'} + × +
+
+
正在加载PDF...
+
+ +