certPreview.vue 5.26 KB
<template>
  <view class="preview-container">
    <!-- 加载提示 -->
    <view class="loading-tip" v-if="loading">
      <view class="loading-icon"></view>
      <text>加载中...</text>
    </view>
    
    <!-- 错误提示 -->
    <view class="error-tip" v-else-if="showError">
      <text class="error-icon">⚠️</text>
      <text class="error-text">{{ errorMsg }}</text>
      <view class="retry-btn" @click="retryLoad">重试</view>
    </view>

    <!-- 方式1:使用 web-view(备选) -->
    <web-view 
      v-if="pdfUrl && !showError && useWebView" 
      :src="pdfUrl"
      @message="onWebViewMessage"
    ></web-view>
  </view>
</template>

<script setup>
import { ref, onUnmounted } from "vue";
import { onLoad } from "@dcloudio/uni-app";
	import config from "@/config.js";

const pdfUrl = ref("");
const loading = ref(true);
const showError = ref(false);
const errorMsg = ref("");
const useWebView = ref(false); // 是否使用 web-view 降级方案
let timeoutTimer = null;

onLoad((option) => {
  if (option.url) {
    // 解码并拼接完整URL
    let relativeUrl = decodeURIComponent(option.url);
    let fullUrl = config.baseUrl_api  + relativeUrl;
    
    console.log("完整PDF地址:", fullUrl);
    
    // 优先使用 openDocument 方式(兼容性最好)
    openPdfWithDocument(fullUrl);
  } else {
    loading.value = false;
    showError.value = true;
    errorMsg.value = "参数错误";
  }
});

// 优先方案:使用 uni.openDocument(推荐,兼容性最好)
const openPdfWithDocument = (url) => {
  // 设置超时
  timeoutTimer = setTimeout(() => {
    if (loading.value) {
      loading.value = false;
      showError.value = true;
      errorMsg.value = "加载超时,请检查网络";
    }
  }, 15000);
  
  // 先下载文件
  uni.downloadFile({
    url: url,
    success: (res) => {
      clearTimeout(timeoutTimer);
      
      if (res.statusCode === 200) {
        const filePath = res.tempFilePath;
        // 打开文档
        uni.openDocument({
          filePath: filePath,
          success: () => {
            // 打开成功,关闭当前页面
            loading.value = false;
            setTimeout(() => {
              uni.navigateBack();
            }, 500);
          },
          fail: (err) => {
            console.error("openDocument失败:", err);
            // 降级到 web-view 方式
            fallbackToWebView(url);
          }
        });
      } else {
        fallbackToWebView(url);
      }
    },
    fail: (err) => {
      console.error("下载失败:", err);
      clearTimeout(timeoutTimer);
      // 降级到 web-view 方式
      fallbackToWebView(url);
    }
  });
};

// 降级方案:使用 web-view
const fallbackToWebView = (url) => {
  console.log("降级使用 web-view 方式");
  useWebView.value = true;
  
  // 处理URL,确保是HTTPS
  let webViewUrl = url;
  if (webViewUrl.startsWith('http://')) {
    webViewUrl = webViewUrl.replace('http://', 'https://');
  }
  
  // 添加时间戳避免缓存问题
  webViewUrl = webViewUrl + (webViewUrl.includes('?') ? '&' : '?') + 't=' + Date.now();
  
  pdfUrl.value = webViewUrl;
  
  // 给 web-view 一些加载时间
  setTimeout(() => {
    if (loading.value) {
      loading.value = false;
      // 不立即显示错误,让 web-view 继续尝试
      setTimeout(() => {
        if (loading.value === false && !showError.value) {
          // 如果还在加载状态,可能是真的有问题
          // 但这里不做额外处理
        }
      }, 3000);
    }
  }, 2000);
};

// web-view 消息接收(可选)
const onWebViewMessage = (e) => {
  console.log("web-view message:", e.detail);
};

// 重试加载
const retryLoad = () => {
  showError.value = false;
  loading.value = true;
  useWebView.value = false;
  errorMsg.value = "";
  
  // 重新获取URL
  const pages = getCurrentPages();
  const currentPage = pages[pages.length - 1];
  const url = currentPage.$page.options.url;
  
  if (url) {
    let fullUrl = BASE_URL + decodeURIComponent(url);
    openPdfWithDocument(fullUrl);
  }
};

// 页面卸载时清除定时器
onUnmounted(() => {
  if (timeoutTimer) clearTimeout(timeoutTimer);
});
</script>

<style lang="scss" scoped>
.preview-container {
  width: 100vw;
  height: 100vh;
  background: #f5f5f5;
  position: relative;
}

.loading-tip {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20rpx;
  
  .loading-icon {
    width: 60rpx;
    height: 60rpx;
    border: 4rpx solid #e0e0e0;
    border-top-color: #AD181F;
    border-radius: 50%;
    animation: rotate 1s linear infinite;
  }
  
  text {
    font-size: 28rpx;
    color: #666;
  }
}

@keyframes rotate {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

.error-tip {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20rpx;
  
  .error-icon {
    font-size: 80rpx;
  }
  
  .error-text {
    font-size: 28rpx;
    color: #C40F18;
  }
  
  .retry-btn {
    margin-top: 30rpx;
    padding: 16rpx 40rpx;
    background: #AD181F;
    color: #fff;
    border-radius: 40rpx;
    font-size: 28rpx;
  }
}

web-view {
  width: 100%;
  height: 100%;
}
</style>