一、上傳
1、html
<template><button @click="takeFile">pdf上傳</button>
</template>
2、JS
takeFile() {// #ifdef H5// H5端使用input方式選擇文件const input = document.createElement('input');input.type = 'file';input.accept = '.pdf';input.onchange = (e) => {if (e.target.files && e.target.files[0]) {this.handleSelectedFile(e.target.files[0]);}};input.click();// #endif// #ifdef MP-WEIXIN// 微信小程序選擇文件wx.chooseMessageFile({count: 1,type: 'file',extension: ['.pdf'],success: (res) => {if (res.tempFiles && res.tempFiles[0]) {this.handleSelectedFile(res.tempFiles[0]);}},fail: (err) => {console.error('選擇文件失敗:', err);this.showToast('選擇文件失敗,請重試');}});// #endif// #ifdef APP-PLUSif (plus.os.name === 'Android') {this.selectPdfAndroid();} else {this.selectPdfIOS();}// #endif},// Android端使用Intent選擇PDFselectPdfAndroid() {try {const Intent = plus.android.importClass('android.content.Intent');const Activity = plus.android.runtimeMainActivity();const intent = new Intent(Intent.ACTION_GET_CONTENT);intent.setType('application/pdf');intent.addCategory(Intent.CATEGORY_OPENABLE);Activity.startActivityForResult(intent, 1001);Activity.onActivityResult = (requestCode, resultCode, data) => {if (requestCode === 1001 && resultCode === Activity.RESULT_OK && data) {const uri = data.getData();this.handleAndroidUri(uri);}};} catch (error) {console.log('Android Intent失敗:', error);this.showToast('當前設備不支持文件選擇');}},// 處理Android URI - 簡化版本handleAndroidUri(uri) {try {const uriString = uri.toString();console.log('選擇的文件URI:', uriString);// 簡化處理:生成默認文件名,直接上傳const timestamp = Date.now();const fileName = `document_${timestamp}.pdf`;console.log('準備上傳文件:', { fileName, uriString });// 直接使用URI進行上傳,不獲取詳細信息this.selectedFile = {path: uriString,name: fileName,size: 0 // 不獲取大小,讓服務器端驗證};this.uploadPdf();} catch (error) {console.log('處理URI失敗:', error);this.showToast('文件處理失敗,請重試');}},// iOS端選擇PDFselectPdfIOS() {// iOS可以使用plus.gallery,設置過濾器plus.gallery.pick((path) => {const fileName = path.split('/').pop() || 'document.pdf';if (fileName.toLowerCase().endsWith('.pdf')) {this.selectedFile = {path: path,name: fileName,size: 0};this.uploadPdf();} else {this.showToast('請選擇PDF文件');}},(error) => {console.log('iOS選擇失敗:', error);this.showToast('文件選擇失敗');},{filter: 'file'});},/*** 處理選中的文件*/handleSelectedFile(file) {// 驗證文件類型if (!this.isPdfFile(file)) {this.showToast('請選擇PDF格式的文件');return;}// 驗證文件大小 (5MB)if (file.size > 5 * 1024 * 1024) {this.showToast('文件大小不能超過5MB');return;}console.log('pdf', file)this.selectedFile = file;// this.uploadProgress = 0;this.uploadPdf();},/*** 驗證是否為PDF文件*/isPdfFile(file) {// #ifdef H5return file.type === 'application/pdf' || file.name.endsWith('.pdf');// #endif// #ifdef MP-WEIXIN || APP-PLUSreturn file.name.endsWith('.pdf');// #endif},/*** 顯示提示消息*/showToast(message) {uni.showToast({title: message,icon: 'none'})}
3、打包配置
/* android打包配置 */"android" : {"permissions" : ["<uses-feature android:name=\"android.hardware.camera\"/>","<uses-feature android:name=\"android.hardware.camera.autofocus\"/>","<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>","<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>","<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>","<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>","<uses-permission android:name=\"android.permission.CALL_PHONE\"/>","<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>","<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>","<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>","<uses-permission android:name=\"android.permission.INTERNET\"/>","<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>","<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>","<uses-permission android:name=\"android.permission.READ_CONTACTS\"/>","<uses-permission android:name=\"android.permission.READ_LOGS\"/>","<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>","<uses-permission android:name=\"android.permission.VIBRATE\"/>","<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>","<uses-permission android:name=\"android.permission.WRITE_CONTACTS\"/>","<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>","<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>","<uses-permission android:name=\"android.permission.RECORD_AUDIO\" />","<uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>","<uses-permission android:name=\"android.permission.MANAGE_EXTERNAL_STORAGE\"/>","<uses-permission android:name=\"android.permission.REQUEST_INSTALL_PACKAGES\"/>"],"abiFilters" : []},/* ios打包配置 */"ios" : {"idfa" : false,"privacyDescription" : {"NSMicrophoneUsageDescription" : "用戶上傳視頻時需使用音頻信息","NSSpeechRecognitionUsageDescription" : "請允許使用您的語音識別,以便更好的體驗該應用","NSPhotoLibraryUsageDescription" : "需要用與評論上傳,頭像上傳功能","NSPhotoLibraryAddUsageDescription" : "保存圖片到本地","NSFaceIDUsageDescription" : "使用面部識別進行登錄","NSCameraUsageDescription" : "需要用與掃描二維碼和圖片拍攝","NSLocationWhenInUseUsageDescription" : "獲得地理位置,為您推薦最近的好友,允許嗎?","NSLocationAlwaysUsageDescription" : "需要您同意才能獲得您的地理位置,允許嗎?","NSLocationAlwaysAndWhenInUseUsageDescription" : "需要一直獲取您的地理位置信息,允許嗎?"},"dSYMs" : false},
二、預覽
1、預覽組件
<template><view class="container"><!-- 導航欄 --><!-- <view class="navbar"><view class="back-btn" @click="handleBack"><uni-icons type="left" size="24" color="#333"></uni-icons></view><view class="title">{{ title || 'PDF預覽' }}</view><view class="right-space"></view></view> --><!-- PDF web-view組件:- 用于加載PDF預覽頁面- 注意:src需要在manifest.json中配置web-view的域名白名單--><web-view :src="webviewUrl" @message="handleWebviewMessage" class="webview":webview-styles="webviewStyles"></web-view><!-- 加載提示 --><!-- <view v-if="loading" class="loading-mask"><view class="loading-container"><uni-loading type="spinner" size="24"></uni-loading><view class="loading-text">加載中...</view></view></view> --></view>
</template><script>
export default {data() {return {title: "", // PDF標題pdfUrl: "", // PDF文件地址webviewUrl: "", // web-view加載的URLloading: true, // 加載狀態webviewStyles: {// width: '100%',// height: '90%'},};},onLoad(options) {// 獲取頁面參數this.title = options.title || "";this.pdfUrl = decodeURIComponent(options.url || "");// 驗證PDF URLif (!this.pdfUrl) {uni.showToast({title: "缺少PDF文件地址",icon: "none",duration: 2000,});setTimeout(() => {this.handleBack();}, 2000);return;}// #ifdef H5window.addEventListener('message', (e) => {// console.log(e,e.data.data[0].action)this.dealMessage(e.data.data[0])})// #endif// 初始化web-view URLthis.initWebviewUrl();},methods: {/*** 初始化web-view加載的URL*/initWebviewUrl() {// 構建PDF預覽頁面的URL(使用hybrid中的html頁面)// 注意:根據實際項目目錄結構調整路徑let viewerUrl = "/static/html/pdfViewer.html";// 處理不同平臺的路徑// #ifdef H5// 添加參數:PDF地址和標題const params = new URLSearchParams();params.append("url", this.pdfUrl);params.append("title", this.title);console.log(params);// H5平臺需要完整路徑viewerUrl = location.origin + viewerUrl + "?" + params.toString();// #endif// #ifndef H5// 非H5平臺直接拼接參數// 添加參數:PDF地址和標題viewerUrl =viewerUrl + "?" + "url=" + this.pdfUrl + "&title=" + this.title;// #endifthis.webviewUrl = viewerUrl;},/*** 處理web-view發送的消息*/handleWebviewMessage(e) {console.log(e)this.dealMessage(e.detail.data[0][0])},dealMessage(data) {if (data.action === "back") {// 處理返回事件this.handleBack();} else if (data.action === "loaded") {// 處理PDF加載完成事件this.loading = false;} else if (data.action === "error") {// 處理PDF加載錯誤this.loading = false;uni.showToast({title: "PDF加載失敗",icon: "none",duration: 2000,});}},/*** 處理返回按鈕點擊*/handleBack() {uni.navigateBack({delta: 1, // 返回的頁面數,1表示返回上一級success: function () {console.log("返回上一頁成功");},fail: function (error) {console.error("返回上一頁失敗", error);},});},},
};
</script><style lang="scss" scoped>
.container {display: flex;flex-direction: column;height: 100vh;
}.navbar {display: flex;align-items: center;height: 44px;background-color: #fff;border-bottom: 1px solid #eee;padding: 0 16px;/* #ifndef H5 */padding-top: 44px;height: 88px;/* #endif */box-sizing: border-box;
}.back-btn {width: 44px;height: 44px;display: flex;align-items: center;justify-content: center;
}.title {flex: 1;text-align: center;font-size: 17px;color: #333;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;
}.right-space {width: 44px;height: 44px;
}.webview {flex: 1;width: 100%;
}.loading-mask {position: fixed;top: 44px;left: 0;right: 0;bottom: 0;background-color: rgba(255, 255, 255, 0.8);display: flex;align-items: center;justify-content: center;z-index: 999;
}.loading-container {display: flex;flex-direction: column;align-items: center;
}.loading-text {margin-top: 12px;font-size: 14px;color: #666;
}
</style>
2、預覽頁面
<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8" /><metaname="viewport"content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/><title>PDF預覽</title><!-- 引入PDF.js庫 --><script src="./pdf.min.js"></script><script type="text/javascript" src="./uni.webview.1.5.6.js"></script><style>* {margin: 0;padding: 0;box-sizing: border-box;}body {background-color: #f5f5f5;overflow-x: hidden;}#pdfContainer {padding: 16px;margin-top: 88px;}.pdf-page {width: 100%;margin-bottom: 16px;box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);background-color: #fff;border-radius: 4px;}.loading {position: fixed;top: 50%;left: 50%;transform: translate(-50%, -50%);text-align: center;}.loading-text {margin-top: 16px;font-size: 14px;color: #333;}.error-container {position: fixed;top: 50%;left: 50%;transform: translate(-50%, -50%);text-align: center;padding: 20px;}.error-text {font-size: 16px;color: #f66;margin-bottom: 20px;}.retry-btn {padding: 8px 16px;background: linear-gradient(300deg, #02c6ea 0%, #67f2b1 100%);color: white;border: none;border-radius: 20px;font-size: 14px;cursor: pointer;}.navbar {position: fixed;top: 0;left: 0;width: 100%;z-index: 99999;display: flex;width: 100%;align-items: center;background-color: #fff;border-bottom: 1px solid #eee;/* #ifndef H5 */padding-top: 44px;height: 88px;/* #endif */box-sizing: border-box;}.navbar.h5 {height: 44px;padding: 0 16px;}.back-btn {width: 44px;height: 44px;display: flex;align-items: center;justify-content: center;}.back-icon {width: 9px;height: 15px;background-image: url(./back.png);background-repeat: no-repeat;background-position: 0 0;background-size: 100% 100%;}.title {flex: 1;text-align: center;font-size: 17px;color: #333;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;}.title > div {padding-right: 22px;}.right-space {width: 44px;height: 44px;}</style></head><body><div class="navbar" id="nav"><div class="back-btn" id="back"><div class="back-icon"></div></div><div class="title"><div>PDF預覽</div></div></div><div id="loading" class="loading"><div class="loading-text">加載中...</div></div><div id="error" class="error-container" style="display: none"><div class="error-text">PDF加載失敗</div><button class="retry-btn" id="retryBtn">重試</button></div><div id="pdfContainer"></div><script>let env = null;document.addEventListener("UniAppJSBridgeReady", function () {uni.getEnv(function (res) {env = res;console.log("當前環境:" + JSON.stringify(res));if (res.h5) {const cls = document.getElementById("nav").className;document.getElementById("nav").setAttribute("class", cls + " h5");// console.log(cls)}});});// 向uni-app發送消息function sendMessage(action, data = {}) {console.log(env);if (env.h5) {// H5環境window.parent.postMessage({data: [{ action, ...data }],},"*");} else {uni.postMessage({data: [{ action, ...data }],});}}// 獲取URL參數function getUrlParams() {const params = {};const search = window.location.search.substring(1);const pairs = search.split("&");pairs.forEach((pair) => {const [key, value] = pair.split("=");if (key) {params[key] = decodeURIComponent(value || "");}});return params;}// 初始化PDF預覽async function initPdfViewer() {const params = getUrlParams();let pdfUrl = params.url;if (!pdfUrl) {showError();return;}try {// 處理本地文件路徑(App平臺)if (pdfUrl.startsWith("_doc/") || pdfUrl.startsWith("_downloads/")) {pdfUrl = window.location.origin + pdfUrl;}console.log(pdfUrl);// 配置PDF.jsconst pdfjsLib = window["pdfjs-dist/build/pdf"];pdfjsLib.GlobalWorkerOptions.workerSrc = "./pdf.worker.min.js";// 加載PDF文檔const loadingTask = pdfjsLib.getDocument(pdfUrl);const pdfDoc = await loadingTask.promise;console.log(pdfDoc);// 隱藏加載狀態,通知uni-app加載完成document.getElementById("loading").style.display = "none";sendMessage("loaded");// 渲染所有頁面for (let pageNum = 1; pageNum <= pdfDoc.numPages; pageNum++) {const page = await pdfDoc.getPage(pageNum);// 設置縮放比例以適應移動設備const viewport = page.getViewport({ scale: 1.2 });// 創建畫布元素const canvas = document.createElement("canvas");canvas.className = "pdf-page";document.getElementById("pdfContainer").appendChild(canvas);const context = canvas.getContext("2d");canvas.height = viewport.height;canvas.width = viewport.width;// 渲染頁面const renderContext = {canvasContext: context,viewport: viewport,};await page.render(renderContext).promise;}} catch (error) {console.error("PDF加載失敗:", error);showError();sendMessage("error", { message: error.message });}}// 顯示錯誤狀態function showError() {document.getElementById("loading").style.display = "none";document.getElementById("error").style.display = "block";}// 重試按鈕事件document.getElementById("retryBtn").addEventListener("click", () => {document.getElementById("error").style.display = "none";document.getElementById("loading").style.display = "block";document.getElementById("pdfContainer").innerHTML = "";initPdfViewer();});// 頁面加載完成后初始化window.addEventListener("DOMContentLoaded", initPdfViewer);window.onload = () => {document.getElementById("back").addEventListener("click", () => {console.log("back");sendMessage("back");});};// 監聽頁面返回事件(Android物理返回鍵)document.addEventListener("backbutton",() => {sendMessage("back");},false);</script></body>
</html>