Files
integral-shop/single_uniapp22miao/pages/index/index.vue

440 lines
12 KiB
Vue
Raw Normal View History

<template>
<view class="preview-page">
<!-- 加载提示 -->
<view class="loading-overlay" v-if="loadingPdf && usePdfJs">
<view class="loading-content">
<view class="loading-spinner"></view>
<text class="loading-text">正在加载PDF...</text>
</view>
</view>
<!-- PDF容器 - 移动端使用PDF.js渲染 -->
<view
class="pdf-container"
ref="pdfContainer"
v-if="usePdfJs"
:style="{ display: 'block' }"
></view>
<!-- Web-view - PC端或PDF.js加载失败时使用 -->
<web-view
class="pdf-view"
:src="pdfUrl"
v-if="!usePdfJs"
></web-view>
<!-- 底部操作栏 -->
<view class="fixed-footer">
<view class="footer-text" @click="goToSign">前往签字</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
pdfUrl: '/static/templates-xsj.pdf',
userId: '',
isMobile: false,
usePdfJs: false,
pdfDoc: null,
scale: 1,
loadingPdf: false,
pdfReady: false
}
},
onLoad(options) {
// #ifdef H5
// 移动端检测UA + 视口宽度
const ua = (navigator.userAgent || '').toLowerCase()
const isMobileUA = /mobile|android|iphone|ipad|micromessenger/.test(ua)
const narrowViewport = (window.innerWidth || 0) <= 480
this.isMobile = isMobileUA || narrowViewport
this.usePdfJs = this.isMobile
const getUserIdFromUrl = () => {
let id = ''
const s = window.location.search || ''
if (s) {
const p = new URLSearchParams(s)
id = p.get('user_id') || p.get('userId') || ''
}
if (!id && window.location.hash) {
const hash = window.location.hash
const qIndex = hash.indexOf('?')
if (qIndex !== -1) {
const q = hash.substring(qIndex + 1)
const hp = new URLSearchParams(q)
id = hp.get('user_id') || hp.get('userId') || ''
}
}
if (!id) {
const href = window.location.href || ''
const m = href.match(/[?&]user_id=([^&#]+)/)
if (m) id = decodeURIComponent(m[1])
}
return id
}
this.userId = (options && options.user_id) ? options.user_id : getUserIdFromUrl()
console.log('===userId===', this.userId)
if (this.userId) {
try { localStorage.setItem('user_id', this.userId) } catch(e) {}
if (typeof uni !== 'undefined' && uni.setStorageSync) uni.setStorageSync('user_id', this.userId)
}
// #endif
},
onReady() {
// #ifdef H5
// 使用setTimeout确保toast显示后再跳转
// setTimeout(() => {
// uni.navigateTo({
// url: '/pages/integral/points',
// });
// }, 500);
// return;
if (this.usePdfJs) {
// 延迟初始化确保DOM完全就绪
this.$nextTick(() => {
setTimeout(() => {
this.initPdf()
}, 300)
})
}
// #endif
},
onShow() {
// 页面显示时的逻辑
},
methods: {
// 去搜索页
goToSearch() {
uni.navigateTo({
url: '/pages/sub-pages/search/index'
});
},
// 去签字页
goToSign() {
uni.navigateTo({
url: '/pages/sub-pages/webview/sign?user_id=' + this.userId
})
},
// 加载外部脚本PDF.js避免影响 PC 端
loadScript(src) {
return new Promise((resolve, reject) => {
if (document.querySelector(`script[src="${src}"]`)) return resolve()
const s = document.createElement('script')
s.src = src
s.onload = () => resolve()
s.onerror = (e) => reject(e)
document.head.appendChild(s)
})
},
async initPdf() {
uni.showLoading({
title: '加载PDF中...',
mask: true
})
try {
this.loadingPdf = true
this.pdfReady = false
// 引入 PDF.js 主库与 worker
console.log('开始加载 PDF.js 库...')
await this.loadScript('https://cdn.jsdelivr.net/npm/pdfjs-dist@3.11.174/build/pdf.min.js')
await this.loadScript('https://cdn.jsdelivr.net/npm/pdfjs-dist@3.11.174/build/pdf.worker.min.js')
console.log('PDF.js 库加载完成')
// 多次尝试确保容器就绪
let container = null
let attempts = 0
const maxAttempts = 5
while (!container && attempts < maxAttempts) {
await this.$nextTick()
await new Promise(resolve => setTimeout(resolve, 100))
container = this.ensurePdfContainer()
if (container && container.style) {
console.log('PDF容器已就绪')
break
}
attempts++
console.log(`尝试获取PDF容器 (${attempts}/${maxAttempts})`)
}
if (!container || !container.style) {
console.warn('PDF容器未就绪回退到 web-view')
this.usePdfJs = false
uni.hideLoading()
return
}
const pdfjsLib = window['pdfjsLib']
if (!pdfjsLib) {
console.error('PDF.js 库未加载')
this.usePdfJs = false
uni.hideLoading()
return
}
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@3.11.174/build/pdf.worker.min.js'
// 获取文档
console.log('开始加载 PDF 文档...')
this.pdfDoc = await pdfjsLib.getDocument(this.pdfUrl).promise
console.log('PDF 文档加载成功,开始渲染...')
await this.renderAllPages()
this.pdfReady = true
window.addEventListener('resize', this.handleResize, { passive: true })
uni.hideLoading()
uni.showToast({
title: 'PDF加载成功',
icon: 'success',
duration: 1500
})
} catch (e) {
console.error('PDF 加载失败', e)
uni.hideLoading()
uni.showToast({
title: 'PDF加载失败使用备用方式',
icon: 'none',
duration: 2000
})
// 加载失败时回退到 web-view
this.usePdfJs = false
} finally {
this.loadingPdf = false
}
},
ensurePdfContainer() {
// 首先尝试通过 ref 获取
if (this.$refs && this.$refs.pdfContainer) {
const el = this.$refs.pdfContainer
// 确保是真实的DOM元素
if (el && el.nodeType === 1) {
return el
}
}
// 尝试通过类名查找
if (typeof document !== 'undefined') {
let container = document.querySelector('.pdf-container')
if (container && container.nodeType === 1) {
return container
}
// 如果容器不存在,创建一个
const parent = document.querySelector('.preview-page')
if (parent) {
const footer = parent.querySelector('.fixed-footer')
const created = document.createElement('div')
created.className = 'pdf-container'
created.style.cssText = 'position:absolute;top:0;left:0;right:0;bottom:120rpx;overflow-y:auto;overflow-x:hidden;background:#fff;'
if (footer) {
parent.insertBefore(created, footer)
} else {
parent.appendChild(created)
}
console.log('PDF容器已创建')
return created
}
}
return null
},
getContainer() {
// 优先使用 ref
if (this.$refs && this.$refs.pdfContainer) {
const el = this.$refs.pdfContainer
if (el && el.nodeType === 1) {
return el
}
}
// 其次使用选择器
if (typeof document !== 'undefined') {
return document.querySelector('.pdf-container')
}
return null
},
async renderAllPages() {
if (!this.usePdfJs || !this.pdfDoc) {
console.log('取消渲染usePdfJs=', this.usePdfJs, 'pdfDoc=', !!this.pdfDoc)
return
}
// 确保容器存在
let container = this.getContainer()
if (!container) {
await this.$nextTick()
container = this.ensurePdfContainer()
}
if (!container || !container.style) {
console.warn('PDF容器不可用回退到 web-view')
this.usePdfJs = false
return
}
console.log('开始渲染PDF共', this.pdfDoc.numPages, '页')
// 清空容器
container.innerHTML = ''
container.style.overflowY = 'auto'
container.style.overflowX = 'hidden'
container.style.webkitOverflowScrolling = 'touch'
// 获取容器宽度
const cw = container.clientWidth || window.innerWidth || 375
try {
// 渲染所有页面
for (let i = 1; i <= this.pdfDoc.numPages; i++) {
const page = await this.pdfDoc.getPage(i)
const viewport = page.getViewport({ scale: this.scale })
const scaleToFit = cw / viewport.width
const vp = page.getViewport({ scale: this.scale * scaleToFit })
// 创建canvas
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
canvas.width = vp.width
canvas.height = vp.height
canvas.style.width = '100%'
canvas.style.height = `${vp.height}px`
canvas.style.display = 'block'
canvas.style.margin = '0 auto 12px'
container.appendChild(canvas)
// 渲染页面
await page.render({ canvasContext: ctx, viewport: vp }).promise
console.log(`PDF 第 ${i} 页渲染完成`)
}
console.log('PDF 全部渲染完成')
} catch (error) {
console.error('渲染PDF时出错:', error)
uni.showToast({
title: '渲染失败',
icon: 'none'
})
}
},
handleResize() {
// 响应式:窗口变化时重新按容器宽度渲染
if (this.usePdfJs && this.pdfDoc) {
this.$nextTick(() => this.renderAllPages())
}
}
}
}
</script>
<style lang="scss" scoped>
.preview-page {
position: relative;
height: 100vh;
background: #f5f5f5;
}
.loading-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(255, 255, 255, 0.9);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
}
.loading-content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.loading-spinner {
width: 80rpx;
height: 80rpx;
border: 6rpx solid #f3f3f3;
border-top: 6rpx solid #f30303;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-text {
margin-top: 30rpx;
font-size: 28rpx;
color: #666666;
}
.pdf-view {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 120rpx;
}
.pdf-container {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 120rpx;
overflow-y: auto;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;
background: #fff;
}
.fixed-footer {
position: fixed;
left: 0;
right: 0;
bottom: 0;
height: 120rpx;
background: #ffffff;
border-top: 2rpx solid #eee;
display: flex;
align-items: center;
justify-content: center;
padding: 0 30rpx;
z-index: 10;
}
.footer-text {
flex: 1;
background: #f30303;
color: #fff;
font-size: 32rpx;
padding: 18rpx 30rpx;
border-radius: 20rpx;
text-align: center;
box-shadow: 0 8rpx 20rpx rgba(255, 45, 45, 0.25);
}
</style>