Commit 0c943aff by Sweet Zhang

解决PDF打开缓慢的问题

parent 969f8366
<template>
<!-- 横屏提示 -->
<view
v-if="showTip && isPortrait"
class="landscape-tip"
>
<view class="tip-content">
<!-- 关闭按钮 -->
<view class="close-btn" @tap="handleCloseTip">
<text class="close-icon">×</text>
</view>
<view class="tip-icon">🔄</view>
<text class="tip-text">{{ tipText }}</text>
<text class="tip-debug" v-if="debug">方向: {{ screenOrientation }}, 窗口: {{ windowSize }}</text>
<!-- 操作按钮区域 -->
<!-- <view class="action-buttons">
<button class="action-btn secondary" @tap="handleCloseTip">
<text>暂不横屏</text>
</button>
<button class="action-btn primary" @tap="handleRotate">
<text>立即横屏</text>
</button>
</view> -->
</view>
</view>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted } from 'vue';
// ========================== Props & Emits ==========================
interface Props {
autoShow?: boolean;
showDelay?: number;
tipText?: string;
autoHide?: boolean;
autoHideDelay?: number;
checkWideContent?: boolean;
wideContentThreshold?: number;
debug?: boolean;
showRotateButton?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
autoShow: true,
showDelay: 2000,
tipText: '建议横屏观看以获得更好体验',
autoHide: true,
autoHideDelay: 5000,
checkWideContent: true,
wideContentThreshold: 1.5,
debug: false,
showRotateButton: true
});
const emit = defineEmits<{
close: [];
show: [];
rotate: [];
orientationChange: [orientation: 'portrait' | 'landscape'];
}>();
// ========================== 响应式数据 ==========================
const showTip = ref(false);
const isPortrait = ref(true);
const hasShownTip = ref(false);
const autoHideTimer = ref<number | null>(null);
const windowSize = ref('');
// ========================== 计算属性 ==========================
const screenOrientation = computed(() => {
return isPortrait.value ? '竖屏' : '横屏';
});
// ========================== 生命周期 ==========================
onMounted(() => {
if (props.debug) console.log('AutoLandscapeTip: 组件已挂载');
initOrientationDetection();
if (props.autoShow) {
if (props.debug) console.log('AutoLandscapeTip: 开始延迟显示检测');
setTimeout(() => {
checkAndShowTip();
}, props.showDelay);
}
});
onUnmounted(() => {
removeOrientationListener();
clearAutoHideTimer();
});
// ========================== 方向检测方法 ==========================
/**
* 初始化方向检测
*/
const initOrientationDetection = () => {
checkScreenOrientation();
if (typeof uni !== 'undefined' && uni.onWindowResize) {
uni.onWindowResize(handleResize);
} else if (typeof window !== 'undefined') {
window.addEventListener('resize', handleResize);
}
if (typeof window !== 'undefined' && window.screen && window.screen.orientation) {
window.screen.orientation.addEventListener('change', handleOrientationChange);
}
};
/**
* 移除方向监听
*/
const removeOrientationListener = () => {
if (typeof uni !== 'undefined' && uni.offWindowResize) {
uni.offWindowResize(handleResize);
} else if (typeof window !== 'undefined') {
window.removeEventListener('resize', handleResize);
}
if (typeof window !== 'undefined' && window.screen && window.screen.orientation) {
window.screen.orientation.removeEventListener('change', handleOrientationChange);
}
};
/**
* 处理系统方向变化
*/
const handleOrientationChange = () => {
if (props.debug) console.log('AutoLandscapeTip: 系统方向变化');
checkScreenOrientation();
};
/**
* 处理窗口大小变化
*/
const handleResize = (res?: any) => {
const width = res?.size?.windowWidth || window.innerWidth;
const height = res?.size?.windowHeight || window.innerHeight;
windowSize.value = `${width} × ${height}`;
if (props.debug) console.log('AutoLandscapeTip: 窗口尺寸', width, height);
checkScreenOrientation();
};
/**
* 检测屏幕方向
*/
const checkScreenOrientation = () => {
let width = window.innerWidth;
let height = window.innerHeight;
if (typeof uni !== 'undefined' && uni.getSystemInfoSync) {
try {
const systemInfo = uni.getSystemInfoSync();
width = systemInfo.windowWidth || systemInfo.screenWidth || width;
height = systemInfo.windowHeight || systemInfo.screenHeight || height;
} catch (e) {
if (props.debug) console.log('AutoLandscapeTip: 获取系统信息失败', e);
}
}
const newIsPortrait = height > width;
if (props.debug) console.log('AutoLandscapeTip: 方向检测', {
width, height,
current: isPortrait.value,
new: newIsPortrait
});
if (isPortrait.value !== newIsPortrait) {
isPortrait.value = newIsPortrait;
emit('orientationChange', isPortrait.value ? 'portrait' : 'landscape');
if (props.debug) console.log('AutoLandscapeTip: 方向变化', screenOrientation.value);
if (!newIsPortrait && showTip.value) {
if (props.debug) console.log('AutoLandscapeTip: 横屏,自动隐藏提示');
handleCloseTip();
}
if (newIsPortrait && !hasShownTip.value) {
if (props.debug) console.log('AutoLandscapeTip: 竖屏,检查是否显示提示');
setTimeout(() => {
checkAndShowTip();
}, 500);
}
}
};
/**
* 检查并显示提示
*/
const checkAndShowTip = async () => {
if (hasShownTip.value || !isPortrait.value) {
if (props.debug) console.log('AutoLandscapeTip: 条件不满足,跳过显示');
return;
}
if (props.debug) console.log('AutoLandscapeTip: 开始检查显示条件');
let shouldShow = true;
if (props.checkWideContent) {
shouldShow = await checkWideContent();
if (props.debug) console.log('AutoLandscapeTip: 宽内容检测结果', shouldShow);
}
if (shouldShow) {
showLandscapeTip();
}
};
/**
* 检测宽内容
*/
const checkWideContent = (): Promise<boolean> => {
return new Promise((resolve) => {
const scrollWidth = document.documentElement.scrollWidth || document.body.scrollWidth;
const windowWidth = window.innerWidth;
if (scrollWidth > 0 && windowWidth > 0) {
const isWide = scrollWidth > windowWidth * props.wideContentThreshold;
if (props.debug) console.log('AutoLandscapeTip: 文档宽度检测', { scrollWidth, windowWidth, isWide });
resolve(isWide);
return;
}
if (typeof uni !== 'undefined' && uni.createSelectorQuery) {
const query = uni.createSelectorQuery();
query.selectViewport().scrollOffset().exec((res: any) => {
if (res && res[0]) {
const scrollWidth = res[0].scrollWidth || 0;
const windowWidth = window.innerWidth;
const isWide = scrollWidth > windowWidth * props.wideContentThreshold;
resolve(isWide);
} else {
resolve(true);
}
});
} else {
resolve(true);
}
});
};
/**
* 显示横屏提示
*/
const showLandscapeTip = () => {
if (hasShownTip.value || !isPortrait.value) {
return;
}
if (props.debug) console.log('AutoLandscapeTip: 显示横屏提示');
showTip.value = true;
hasShownTip.value = true;
emit('show');
if (props.autoHide) {
clearAutoHideTimer();
autoHideTimer.value = setTimeout(() => {
if (props.debug) console.log('AutoLandscapeTip: 自动隐藏提示');
handleCloseTip();
}, props.autoHideDelay) as unknown as number;
}
};
/**
* 处理关闭提示
*/
const handleCloseTip = () => {
showTip.value = false;
clearAutoHideTimer();
emit('close');
};
/**
* 处理横屏旋转
*/
const handleRotate = () => {
if (props.debug) console.log('AutoLandscapeTip: 用户选择横屏');
emit('rotate');
// 尝试锁定横屏
if (typeof window !== 'undefined' && window.screen && window.screen.orientation && window.screen.orientation.lock) {
window.screen.orientation.lock('landscape').catch((error) => {
console.log('AutoLandscapeTip: 横屏锁定失败', error);
uni.showToast({
title: '请手动旋转设备',
icon: 'none',
duration: 2000
});
});
} else {
uni.showToast({
title: '请手动旋转设备',
icon: 'none',
duration: 2000
});
}
handleCloseTip();
};
/**
* 清除自动隐藏计时器
*/
const clearAutoHideTimer = () => {
if (autoHideTimer.value) {
clearTimeout(autoHideTimer.value);
autoHideTimer.value = null;
}
};
/**
* 手动显示提示(供父组件调用)
*/
const show = () => {
showLandscapeTip();
};
/**
* 手动隐藏提示(供父组件调用)
*/
const hide = () => {
handleCloseTip();
};
/**
* 重置提示状态(可以再次自动显示)
*/
const reset = () => {
hasShownTip.value = false;
handleCloseTip();
};
// 暴露方法给父组件
defineExpose({
show,
hide,
reset,
getOrientation: () => isPortrait.value ? 'portrait' : 'landscape',
isShowing: () => showTip.value
});
</script>
<style scoped lang="scss">
.landscape-tip {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
display: flex;
align-items: center;
justify-content: center;
z-index: 10000;
animation: fadeIn 0.3s ease;
}
.tip-content {
position: relative;
background: #ffffff;
border-radius: 20rpx;
padding: 60rpx 40rpx 40rpx;
margin: 40rpx;
max-width: 500rpx;
text-align: center;
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.3);
animation: slideUp 0.3s ease;
/* 关闭按钮 */
.close-btn {
position: absolute;
top: 20rpx;
right: 20rpx;
width: 60rpx;
height: 60rpx;
border-radius: 50%;
background: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
.close-icon {
font-size: 36rpx;
color: #666;
font-weight: bold;
line-height: 1;
}
&:active {
background: #e0e0e0;
}
}
.tip-icon {
font-size: 80rpx;
display: block;
margin-bottom: 30rpx;
}
.tip-text {
display: block;
font-size: 32rpx;
font-weight: 500;
color: #333;
margin-bottom: 20rpx;
line-height: 1.5;
}
.tip-debug {
display: block;
font-size: 22rpx;
color: #999;
margin-bottom: 30rpx;
}
}
/* 操作按钮区域 */
.action-buttons {
display: flex;
gap: 20rpx;
justify-content: center;
.action-btn {
flex: 1;
padding: 20rpx 30rpx;
border-radius: 10rpx;
font-size: 28rpx;
border: none;
min-width: 0;
&.secondary {
background: #f0f0f0;
color: #666;
}
&.primary {
background: #20269B;
color: white;
}
&:active {
opacity: 0.8;
}
}
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(50rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* 响应式设计 */
@media (max-width: 480px) {
.tip-content {
margin: 20rpx;
padding: 50rpx 30rpx 30rpx;
}
.action-buttons {
flex-direction: column;
gap: 15rpx;
}
}
</style>
\ No newline at end of file
<template>
<view class="pdf-viewer" ref="pdfContainerRef" >
<!-- 横屏提示组件 -->
<LandscapeTip
:debug="false"
:auto-show="pdfInfo.landscapeFlag ? pdfInfo.landscapeFlag : false"
:show-delay="1000"
:check-wide-content="false"
/>
<!-- 添加一个滚动容器 -->
<scroll-view
class="pdf-scroll-view"
scroll-y
:show-scrollbar="false"
@scroll="handleScroll"
:scroll-top="scrollTop"
>
<!-- PDF文档信息 -->
<view class="pdf-info" v-if="pdfInfo.title">
<text class="pdf-title">{{ pdfInfo.title }}</text>
<text class="pdf-page-count" v-if="pdfPageCount > 0">{{ pdfPageCount }}</text>
</view>
<!-- 页面列表 -->
<view
v-for="pageIndex in pdfPageCount"
:key="pageIndex"
class="page-container"
:id="`page-${pageIndex}`"
>
<view class="page-header" v-if="loadingStatus">
<text class="page-number">{{ pageIndex }}</text>
<text class="page-status" v-if="isPageLoading(pageIndex)">加载中...</text>
<text class="page-status error" v-else-if="isPageFailed(pageIndex)">加载失败</text>
<text class="page-status success" v-else-if="getPageImage(pageIndex)">加载完成</text>
</view>
<view class="page-content">
<view class="loadEffect" v-if="!getPageImage(pageIndex) || isPageLoading(pageIndex)"></view>
<image
v-if="getPageImage(pageIndex)"
:src="getPageImage(pageIndex)"
mode="widthFix"
class="pdf-image"
@load="handlePageImageLoad(pageIndex)"
@error="handlePageImageError(pageIndex)"
:show-menu-by-longpress="false"
></image>
<view v-else-if="isPageFailed(pageIndex)" class="page-error" @click="retryLoadPage(pageIndex)">
<text class="error-text">页面加载失败,点击重试</text>
<text class="retry-count">已重试 {{ getPageRetryCount(pageIndex) }}</text>
</view>
<view v-else class="page-placeholder">
<text>页面加载中...</text>
</view>
</view>
</view>
<!-- 加载状态 -->
<view v-if="loading" class="loading-state">
<view class="loading-spinner"></view>
<text>文件较大,正在加载中...</text>
</view>
<!-- 错误状态 -->
<view v-if="error" class="error-state">
<text class="error-icon"></text>
<text class="error-message">{{ errorMessage }}</text>
<button class="retry-button" @click="initPdf">重试</button>
</view>
<!-- 加载进度 -->
<view v-if="!loading && !error && pdfPageCount > 0" class="progress-info">
<text>已加载 {{ loadedPages }}/{{ pdfPageCount }}</text>
<view class="progress-bar">
<view class="progress-inner" :style="{ width: `${(loadedPages / pdfPageCount) * 100}%` }"></view>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup lang="ts">
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue';
// 导入本地安装的PDF.js
import * as pdfjsLib from 'pdfjs-dist';
import pdfjsWorker from 'pdfjs-dist/build/pdf.worker?url';
import LandscapeTip from '@/components/LandscapeTip/LandscapeTip.vue';
// ========================== 类型定义 ==========================
interface PdfInfo {
title?: string;
url: string;
landscapeFlag?:boolean;
}
interface Props {
pdfInfo: PdfInfo;
autoLoad?: boolean;
lazyLoad?: boolean;
maxRetryCount?: number;
loadingStatus?:boolean;
}
// ========================== Props & Emits ==========================
const props = withDefaults(defineProps<Props>(), {
autoLoad: true,
lazyLoad: true,
maxRetryCount: 3,
loadingStatus:false,
});
const emit = defineEmits<{
loadStart: [url: string];
loadComplete: [url: string, pageCount: number];
loadError: [url: string, error: Error];
pageChange: [currentPage: number, totalPages: number];
}>();
// ========================== 响应式数据 ==========================
const pdfImages = ref<string[]>([]);
const imgLoading = ref<boolean[]>([]);
const pdfPageCount = ref(0);
const currentLoading = ref(0);
const pdfDoc = ref<any>(null);
const failedPages = ref<Record<number, number>>({});
const loadingQueue = ref<number[]>([]);
const isProcessingQueue = ref(false);
const loading = ref(false);
const error = ref(false);
const errorMessage = ref('');
const currentPage = ref(1);
const lastScrollTime = ref(0);
const scrollThrottle = ref(300);
const loadedPageSet = ref<Set<number>>(new Set()); // 记录已加载的页面
// 添加 scrollTop 用于控制滚动位置
const scrollTop = ref(0);
// ========================== 初始化PDF.js ==========================
// 设置worker
pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsWorker;
// ========================== 横屏提示处理 ==========================
const autoLandscapeTipRef = ref();
/**
* 提示关闭回调
*/
const onTipClose = () => {
console.log('横屏提示已关闭');
};
/**
* 提示显示回调
*/
const onTipShow = () => {
console.log('横屏提示已显示');
};
/**
* 方向变化回调
*/
const onOrientationChange = (orientation: 'portrait' | 'landscape') => {
console.log('屏幕方向变为:', orientation);
};
// ========================== 计算属性 ==========================
const loadedPages = computed(() => {
return loadedPageSet.value.size;
});
const hasMoreToLoad = computed(() => {
return loadedPages.value < pdfPageCount.value;
});
// ========================== 生命周期 ==========================
onMounted(() => {
if (props.autoLoad) {
initPdf();
}
});
onUnmounted(() => {
cleanup();
});
// ========================== 监听器 ==========================
watch(() => props.pdfInfo.url, (newUrl, oldUrl) => {
if (newUrl && newUrl !== oldUrl) {
resetState();
initPdf();
}
});
// ========================== 公共方法 ==========================
/**
* 初始化PDF
*/
const initPdf = async () => {
if (!props.pdfInfo.url) {
setError('PDF URL不能为空');
return;
}
try {
resetState();
loading.value = true;
error.value = false;
emit('loadStart', props.pdfInfo.url);
await loadPdfDocument();
// 初始加载前3页
const initialPages = Math.min(3, pdfPageCount.value);
console.log(`初始加载前 ${initialPages} 页`);
for (let i = 1; i <= initialPages; i++) {
addToLoadingQueue(i);
}
processLoadingQueue();
// 延迟检查其他可见页面
nextTick(() => {
setTimeout(() => {
checkVisiblePages();
}, 800);
});
} catch (err: any) {
setError('文件读取失败')
// setError(`PDF初始化失败: ${err.message}`);
emit('loadError', props.pdfInfo.url, err);
} finally {
loading.value = false;
}
};
/**
* 重新加载PDF
*/
const reload = () => {
initPdf();
};
// ========================== 内部方法 ==========================
/**
* 重置状态
*/
const resetState = () => {
pdfImages.value = [];
imgLoading.value = [];
pdfPageCount.value = 0;
currentLoading.value = 0;
failedPages.value = {};
loadingQueue.value = [];
isProcessingQueue.value = false;
error.value = false;
errorMessage.value = '';
currentPage.value = 1;
loadedPageSet.value.clear();
if (pdfDoc.value) {
pdfDoc.value.destroy();
pdfDoc.value = null;
}
};
/**
* 清理资源
*/
const cleanup = () => {
if (pdfDoc.value) {
pdfDoc.value.destroy();
pdfDoc.value = null;
}
};
/**
* 设置错误状态
*/
const setError = (message: string) => {
error.value = true;
errorMessage.value = message;
loading.value = false;
};
/**
* 加载PDF文档
*/
const loadPdfDocument = async (): Promise<number> => {
try {
const loadingTask = pdfjsLib.getDocument({
url: props.pdfInfo.url,
cMapUrl: 'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.11.338/cmaps/',
cMapPacked: true,
disableFontFace: true,
useSystemFonts: true,
isEvalSupported: false,
});
pdfDoc.value = await loadingTask.promise;
pdfPageCount.value = pdfDoc.value.numPages;
// 初始化数组
pdfImages.value = new Array(pdfPageCount.value).fill('');
imgLoading.value = new Array(pdfPageCount.value).fill(false);
emit('loadComplete', props.pdfInfo.url, pdfPageCount.value);
console.log(`PDF文档加载完成: ${props.pdfInfo.url}, 共 ${pdfPageCount.value} 页`);
return pdfPageCount.value;
} catch (err: any) {
console.error('PDF文档加载失败:', err);
throw new Error(`文档加载失败: ${err.message}`);
}
};
/**
* 滚动处理 - 使用 scroll-view 的 scroll 事件
*/
const handleScroll = (e: any) => {
if (!props.lazyLoad || loading.value) return;
const now = Date.now();
if (now - lastScrollTime.value < scrollThrottle.value) {
return;
}
lastScrollTime.value = now;
// 使用防抖
clearTimeout((window as any).scrollTimer);
(window as any).scrollTimer = setTimeout(() => {
checkVisiblePages(e.detail.scrollTop);
}, 100);
};
/**
* 检查可见页面 - 修改为接收 scrollTop 参数
*/
const checkVisiblePages = (scrollTop: number) => {
if (pdfPageCount.value === 0 || loadingQueue.value.length > 5) return;
console.log('开始检查可见页面...', scrollTop);
const windowHeight = uni.getSystemInfoSync().windowHeight;
// 计算可见区域
const visibleTop = scrollTop - 500; // 提前500px开始加载
const visibleBottom = scrollTop + windowHeight + 1000; // 延后1000px加载
// 检查每个页面是否在可见区域内
for (let i = 1; i <= pdfPageCount.value; i++) {
// 如果页面已经加载或正在加载,跳过
if (loadedPageSet.value.has(i) || isPageLoading(i)) {
continue;
}
// 检查页面位置
uni.createSelectorQuery()
.select(`#page-${i}`)
.boundingClientRect((rect: any) => {
if (rect) {
const pageTop = scrollTop + rect.top;
const pageBottom = scrollTop + rect.bottom;
// 如果页面在可见区域内
if (pageBottom > visibleTop && pageTop < visibleBottom) {
console.log(`页面 ${i} 在可见区域内,准备加载`);
addToLoadingQueue(i);
}
// 更新当前页
if (rect.top < windowHeight / 2 && rect.bottom > windowHeight / 2) {
if (currentPage.value !== i) {
currentPage.value = i;
emit('pageChange', i, pdfPageCount.value);
}
}
}
})
.exec();
}
// 处理加载队列
setTimeout(() => {
processLoadingQueue();
}, 50);
};
/**
* 添加到加载队列
*/
const addToLoadingQueue = (pageNumber: number) => {
if (!loadingQueue.value.includes(pageNumber) &&
!loadedPageSet.value.has(pageNumber) &&
!isPageLoading(pageNumber)) {
console.log(`添加页面 ${pageNumber} 到加载队列`);
loadingQueue.value.push(pageNumber);
// 限制队列长度,避免一次性加载太多
if (loadingQueue.value.length > 10) {
loadingQueue.value = loadingQueue.value.slice(0, 10);
}
}
};
/**
* 处理加载队列
*/
const processLoadingQueue = async () => {
if (isProcessingQueue.value || loadingQueue.value.length === 0) return;
isProcessingQueue.value = true;
try {
// 每次处理1页
const pagesToLoad = loadingQueue.value.splice(0, 1);
console.log(`处理加载队列: 加载页面 ${pagesToLoad[0]}`);
for (const pageNumber of pagesToLoad) {
await loadPdfPage(pageNumber);
}
} catch (err) {
console.error('处理加载队列失败:', err);
} finally {
isProcessingQueue.value = false;
// 如果队列中还有任务,继续处理
if (loadingQueue.value.length > 0) {
setTimeout(processLoadingQueue, 200);
} else {
// 队列处理完成后,再次检查可见页面
setTimeout(() => {
checkVisiblePages();
}, 300);
}
}
};
/**
* 加载PDF页面
*/
const loadPdfPage = async (pageNumber: number) => {
if (loadedPageSet.value.has(pageNumber)) return;
const retryCount = getPageRetryCount(pageNumber);
if (retryCount >= props.maxRetryCount) {
console.warn(`页面 ${pageNumber} 已达到最大重试次数`);
return;
}
try {
imgLoading.value[pageNumber - 1] = true;
currentLoading.value++;
console.log(`开始加载页面 ${pageNumber}...`);
if (!pdfDoc.value) {
throw new Error('PDF文档未加载');
}
const page = await pdfDoc.value.getPage(pageNumber);
const viewport = page.getViewport({ scale: 1.8 });
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
if (!context) {
throw new Error('无法获取Canvas上下文');
}
canvas.width = viewport.width;
canvas.height = viewport.height;
const renderContext = {
canvasContext: context,
viewport: viewport
};
await page.render(renderContext).promise;
const imageData = canvas.toDataURL('image/jpeg', 0.85);
pdfImages.value[pageNumber - 1] = imageData;
loadedPageSet.value.add(pageNumber);
console.log(`页面 ${pageNumber} 加载完成,当前已加载: ${Array.from(loadedPageSet.value).join(',')}`);
// 清理
canvas.width = 0;
canvas.height = 0;
// 清除失败记录
if (failedPages.value[pageNumber]) {
delete failedPages.value[pageNumber];
}
} catch (err: any) {
console.error(`页面 ${pageNumber} 加载失败:`, err);
// 记录失败次数
if (!failedPages.value[pageNumber]) {
failedPages.value[pageNumber] = 1;
} else {
failedPages.value[pageNumber]++;
}
// 对有问题的页面生成占位图
if (err.message.includes('private field') || err.message.includes('TypeError')) {
pdfImages.value[pageNumber - 1] = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAwIiBoZWlnaHQ9IjUwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZjhmOWZhIi8+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZvbnQtZmFtaWx5PSJBcmlhbCwgc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgZmlsbD0iIzk5OSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZHk9Ii4zZW0iPuW3suino+eggTwvdGV4dD48L3N2Zz4=';
loadedPageSet.value.add(pageNumber);
}
} finally {
imgLoading.value[pageNumber - 1] = false;
currentLoading.value--;
}
};
/**
* 手动加载下一页
*/
const loadNextPage = () => {
if (pdfPageCount.value === 0) return;
// 找到第一个未加载的页面
for (let i = 1; i <= pdfPageCount.value; i++) {
if (!loadedPageSet.value.has(i) && !isPageLoading(i)) {
console.log(`手动加载页面 ${i}`);
addToLoadingQueue(i);
processLoadingQueue();
break;
}
}
};
/**
* 重试加载页面
*/
const retryLoadPage = (pageNumber: number) => {
const retryCount = getPageRetryCount(pageNumber);
if (retryCount >= props.maxRetryCount) {
uni.showToast({
title: '已达到最大重试次数',
icon: 'none',
duration: 2000
});
return;
}
if (failedPages.value[pageNumber]) {
delete failedPages.value[pageNumber];
}
// 从已加载集合中移除
loadedPageSet.value.delete(pageNumber);
pdfImages.value[pageNumber - 1] = '';
addToLoadingQueue(pageNumber);
processLoadingQueue();
};
// ========================== 辅助方法 ==========================
const getPageImage = (pageIndex: number): string => {
return pdfImages.value[pageIndex - 1] || '';
};
const isPageLoading = (pageIndex: number): boolean => {
return imgLoading.value[pageIndex - 1] || false;
};
const isPageFailed = (pageIndex: number): boolean => {
const retryCount = getPageRetryCount(pageIndex);
return retryCount > 0 && retryCount <= props.maxRetryCount;
};
const getPageRetryCount = (pageIndex: number): number => {
return failedPages.value[pageIndex] || 0;
};
const handlePageImageLoad = (pageIndex: number) => {
console.log(`页面 ${pageIndex} 图片加载完成`);
};
const handlePageImageError = (pageIndex: number) => {
console.error(`页面 ${pageIndex} 图片加载失败`);
};
// 暴露方法给父组件
defineExpose({
initPdf,
reload,
loadNextPage, // 新增手动加载下一页方法
getCurrentPage: () => currentPage.value,
getTotalPages: () => pdfPageCount.value,
getLoadedPages: () => Array.from(loadedPageSet.value),
// 手动控制横屏提示
showLandscapeTip: () => autoLandscapeTipRef.value?.show?.(),
hideLandscapeTip: () => autoLandscapeTipRef.value?.hide?.(),
resetLandscapeTip: () => autoLandscapeTipRef.value?.reset?.()
});
</script>
<style scoped lang="scss">
.pdf-viewer {
width: 100%;
height: 100vh;
background: #ffffff;
display: flex;
flex-direction: column;
}
.pdf-scroll-view {
flex: 1;
height: 0; // 重要:让 scroll-view 正确计算高度
}
.pdf-info {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx;
background: #f8f9fa;
border-bottom: 1rpx solid #e8e8e8;
.pdf-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
flex: 1;
}
.pdf-page-count {
font-size: 26rpx;
color: #666;
background: #e6f7ff;
padding: 8rpx 16rpx;
border-radius: 20rpx;
}
}
.page-container {
margin-bottom: 32rpx;
border-bottom: 1rpx solid #f0f0f0;
&:last-child {
border-bottom: none;
}
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 24rpx;
background: #fafafa;
.page-number {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
.page-status {
font-size: 24rpx;
&.success {
color: #52c41a;
}
&.error {
color: #ff4d4f;
}
}
}
.page-content {
position: relative;
min-height: 400rpx;
.pdf-image {
width: 100%;
height: auto;
display: block;
// 禁用长按菜单
-webkit-touch-callout: none;
-webkit-user-select: none;
user-select: none;
// 禁止长按保存
pointer-events: none;
}
}
.loadEffect {
width: 200rpx;
height: 200rpx;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: url('../../static/range-fullloading/loading.gif') no-repeat center;
background-size: contain;
z-index: 10;
}
.page-error {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 400rpx;
background: #fff2f2;
border: 1rpx dashed #ff4d4f;
border-radius: 8rpx;
color: #ff4d4f;
.error-text {
font-size: 28rpx;
margin-bottom: 16rpx;
}
.retry-count {
font-size: 24rpx;
color: #999;
}
}
.page-placeholder {
display: flex;
align-items: center;
justify-content: center;
height: 400rpx;
background: #f8f9fa;
color: #666;
font-size: 28rpx;
}
.loading-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80rpx 0;
.loading-spinner {
width: 40rpx;
height: 40rpx;
border: 4rpx solid #e8e8e8;
border-top: 4rpx solid #20269B;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 24rpx;
}
text {
color: #666;
font-size: 28rpx;
}
}
.error-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80rpx 0;
text-align: center;
.error-icon {
font-size: 60rpx;
margin-bottom: 24rpx;
}
.error-message {
color: #ff4d4f;
font-size: 28rpx;
margin-bottom: 32rpx;
}
.retry-button {
background: #20269B;
color: white;
border: none;
padding: 16rpx 32rpx;
border-radius: 8rpx;
font-size: 28rpx;
}
}
.progress-info {
padding: 12rpx;
background: #f8f9fa;
text-align: center;
text {
display: block;
color: #666;
font-size: 26rpx;
margin-bottom: 16rpx;
}
}
.progress-bar {
width: 100%;
height: 8rpx;
background: #e8e8e8;
border-radius: 4rpx;
overflow: hidden;
.progress-inner {
height: 100%;
background: #20269B;
transition: width 0.3s ease;
}
}
.action-buttons {
display: flex;
justify-content: space-between;
padding: 20rpx 16rpx;
background: #f8f9fa;
border-top: 1rpx solid #e8e8e8;
.action-btn {
flex: 1;
margin: 0 8rpx;
background: #20269B;
color: white;
border: none;
padding: 16rpx;
border-radius: 8rpx;
font-size: 26rpx;
&:disabled {
background: #ccc;
color: #999;
}
}
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
</style>
\ No newline at end of file
declare module 'pdfjs-dist/build/pdf.worker.entry';
\ No newline at end of file
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
<view><uni-tag :text="product.categoryName" size="small" type="primary"></uni-tag></view> <view><uni-tag :text="product.categoryName" size="small" type="primary"></uni-tag></view>
</view> </view>
<view class="change-product" @click="navigateToPKPage"> <view class="change-product" @click="navigateToPKPage">
<uni-icons type="loop" size="16" color="#007aff"></uni-icons> <uni-icons type="loop" size="16" color="#20269B"></uni-icons>
<view <view
v-if="productPKInfoList.length > 1">更换</view> v-if="productPKInfoList.length > 1">更换</view>
</view> </view>
...@@ -132,7 +132,7 @@ const getGroupIcon = (groupCode) => { ...@@ -132,7 +132,7 @@ const getGroupIcon = (groupCode) => {
// 根据分组编码获取对应的颜色 // 根据分组编码获取对应的颜色
const getGroupColor = (groupCode) => { const getGroupColor = (groupCode) => {
const colorMap = { const colorMap = {
'A': '#007aff', // 基本信息-蓝色 'A': '#20269B', // 基本信息-蓝色
'B': '#00cc66', // 适合人群-绿色 'B': '#00cc66', // 适合人群-绿色
'C': '#ff9900', // 产品特色-橙色 'C': '#ff9900', // 产品特色-橙色
'D': '#cc66ff' // 产品彩页-紫色 'D': '#cc66ff' // 产品彩页-紫色
...@@ -318,7 +318,7 @@ onMounted(() => { ...@@ -318,7 +318,7 @@ onMounted(() => {
position: absolute; position: absolute;
right: 20rpx; right: 20rpx;
background: transparent; background: transparent;
color: #007aff; color: #20269B;
font-size: 26rpx; font-size: 26rpx;
padding: 0 10rpx; padding: 0 10rpx;
} }
...@@ -363,7 +363,7 @@ onMounted(() => { ...@@ -363,7 +363,7 @@ onMounted(() => {
display: flex; display: flex;
align-items: center; align-items: center;
font-size: 24rpx; font-size: 24rpx;
color: #007aff; color: #20269B;
} }
/* 对比内容 */ /* 对比内容 */
.compare-content { .compare-content {
...@@ -441,7 +441,7 @@ onMounted(() => { ...@@ -441,7 +441,7 @@ onMounted(() => {
} }
.brochure-btn { .brochure-btn {
background-color: #007aff; background-color: #20269B;
color: #fff; color: #fff;
font-size: 24rpx; font-size: 24rpx;
padding: 8rpx 15rpx; padding: 8rpx 15rpx;
...@@ -474,7 +474,7 @@ onMounted(() => { ...@@ -474,7 +474,7 @@ onMounted(() => {
align-items: center; align-items: center;
gap: 8rpx; gap: 8rpx;
padding: 6rpx 0; padding: 6rpx 0;
color: #007aff; color: #20269B;
font-size: 24rpx; font-size: 24rpx;
cursor: pointer; cursor: pointer;
word-break: break-all; word-break: break-all;
......
<template> <template>
<view class="container"> <view class="container">
<!-- 头部 -->
<view class="header">
<view class="title">产品佣率查询</view>
</view>
<!-- 搜索区域 --> <!-- 搜索区域 -->
<view class="search-box"> <view class="search-box">
<input <input
...@@ -12,8 +7,10 @@ ...@@ -12,8 +7,10 @@
type="text" type="text"
placeholder="请输入产品名称" placeholder="请输入产品名称"
v-model="searchKeyword" v-model="searchKeyword"
confirm-type="done"
@confirm = "fetchProducts(1)"
/> />
<view class="search-icon" @click="fetchProducts(1)">🔍</view> <view class="search-icon" @click="fetchProducts(1)"><text class="iconfont icon-sousuo"></text></view>
</view> </view>
<!-- 产品列表 --> <!-- 产品列表 -->
...@@ -49,7 +46,7 @@ ...@@ -49,7 +46,7 @@
<!-- 空状态 --> <!-- 空状态 -->
<view class="empty" v-if="!loading && products.length === 0"> <view class="empty" v-if="!loading && products.length === 0">
<text>暂无产品数据</text> <text>{{tips}}</text>
</view> </view>
</view> </view>
</template> </template>
...@@ -61,7 +58,7 @@ import api from '@/api/api'; ...@@ -61,7 +58,7 @@ import api from '@/api/api';
const searchKeyword = ref('') const searchKeyword = ref('')
const products = ref([]) const products = ref([])
const loading = ref(false) const loading = ref(false)
const tips = ref('无默认产品,请输入产品名称进行查询')
// 方法 // 方法
const handleSearch = () => { const handleSearch = () => {
...@@ -77,7 +74,12 @@ const goToCommissionDetail = (product) => { ...@@ -77,7 +74,12 @@ const goToCommissionDetail = (product) => {
// 获取产品数据 // 获取产品数据
const fetchProducts = async (type=0) => { const fetchProducts = async (type=0) => {
loading.value = true if(!searchKeyword.value){
tips.value = '无默认产品,请输入产品名称进行查询' ;
products.value = [];
return;
}
loading.value = true
try { try {
let params = { let params = {
name:searchKeyword.value name:searchKeyword.value
...@@ -99,6 +101,9 @@ const fetchProducts = async (type=0) => { ...@@ -99,6 +101,9 @@ const fetchProducts = async (type=0) => {
if(response.success){ if(response.success){
products.value = response.data.list || []; products.value = response.data.list || [];
if(products.value.length===0){
tips.value = '暂无此产品数据'
}
} }
...@@ -113,7 +118,6 @@ const fetchProducts = async (type=0) => { ...@@ -113,7 +118,6 @@ const fetchProducts = async (type=0) => {
// 生命周期 // 生命周期
onMounted(() => { onMounted(() => {
fetchProducts()
}) })
</script> </script>
...@@ -122,6 +126,8 @@ onMounted(() => { ...@@ -122,6 +126,8 @@ onMounted(() => {
padding: 20rpx; padding: 20rpx;
background-color: #f5f7fa; background-color: #f5f7fa;
min-height: 100vh; min-height: 100vh;
width: 100%;
overflow-x: hidden;
} }
.header { .header {
...@@ -155,6 +161,8 @@ onMounted(() => { ...@@ -155,6 +161,8 @@ onMounted(() => {
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
color: #999; color: #999;
width: 80rpx;
text-align: right;
} }
.product-list { .product-list {
......
...@@ -227,9 +227,9 @@ const handleScroll = (e) => { ...@@ -227,9 +227,9 @@ const handleScroll = (e) => {
// 关键:windowHeight 是设备可视区域高度(px),不含导航栏/底部栏 // 关键:windowHeight 是设备可视区域高度(px),不含导航栏/底部栏
windowHeight.value = sysInfo.windowHeight; windowHeight.value = sysInfo.windowHeight;
// 获取滚动相关参数 // 获取滚动相关参数
const { scrollTop, scrollHeight } = e.detail; const { scrollTop, scrollHeight } = e.detail
// 计算是否到达底部(距离底部200rpx时触发加载) // 计算是否到达底部(距离底部200rpx时触发加载)
if (scrollHeight - scrollTop - windowHeight.value <= 200) { if (scrollHeight - scrollTop - windowHeight.value <= 400) {
fetchProducts(); // 加载更多 fetchProducts(); // 加载更多
} }
}; };
...@@ -607,7 +607,7 @@ onUnmounted(() => { ...@@ -607,7 +607,7 @@ onUnmounted(() => {
.confirm-btn { .confirm-btn {
flex: 1; flex: 1;
height: 80rpx; height: 80rpx;
background-color: #007aff; background-color: #20269B;
color: #fff; color: #fff;
border-radius: 40rpx; border-radius: 40rpx;
font-size: 28rpx; font-size: 28rpx;
...@@ -660,7 +660,7 @@ onUnmounted(() => { ...@@ -660,7 +660,7 @@ onUnmounted(() => {
width: 150rpx; width: 150rpx;
height: 60rpx; height: 60rpx;
font-size: 26rpx; font-size: 26rpx;
background-color: #007aff; background-color: #20269B;
color: #fff; color: #fff;
border-radius: 30rpx; border-radius: 30rpx;
padding: 0; padding: 0;
...@@ -751,7 +751,7 @@ onUnmounted(() => { ...@@ -751,7 +751,7 @@ onUnmounted(() => {
.compare-btn { .compare-btn {
min-width: 200rpx; min-width: 200rpx;
height: 70rpx; height: 70rpx;
background-color: #007aff; background-color: #20269B;
color: #fff; color: #fff;
border-radius: 35rpx; border-radius: 35rpx;
font-size: 28rpx; font-size: 28rpx;
...@@ -798,7 +798,7 @@ onUnmounted(() => { ...@@ -798,7 +798,7 @@ onUnmounted(() => {
/* 3. 筛选按钮样式:选中状态(突出显示) */ /* 3. 筛选按钮样式:选中状态(突出显示) */
.filter-btn-item.active { .filter-btn-item.active {
background-color: #007aff; background-color: #20269B;
color: #fff; color: #fff;
} }
/* 加载中状态 */ /* 加载中状态 */
......
...@@ -18,9 +18,11 @@ ...@@ -18,9 +18,11 @@
"mp-painter": "^1.0.1", "mp-painter": "^1.0.1",
"nanoid": "^4.0.0", "nanoid": "^4.0.0",
"pdf.js": "^0.1.0", "pdf.js": "^0.1.0",
"pdfjs-dist": "^2.11.338",
"qrcode": "^1.5.4", "qrcode": "^1.5.4",
"qrcodejs2": "^0.0.2", "qrcodejs2": "^0.0.2",
"uqrcodejs": "^4.0.7" "uqrcodejs": "^4.0.7",
"vite-plugin-top-level-await": "^1.6.0"
}, },
"devDependencies": { "devDependencies": {
"less": "^4.3.0" "less": "^4.3.0"
...@@ -32,6 +34,999 @@ ...@@ -32,6 +34,999 @@
"integrity": "sha512-DBtk046ofmeFd82zRI7d89SoEwrAxYzUN3WVPm1DIBkpLPG5F5QDNkHMnZGu2wNrMEmGBjBpUh3vqEY1L3jaMw==", "integrity": "sha512-DBtk046ofmeFd82zRI7d89SoEwrAxYzUN3WVPm1DIBkpLPG5F5QDNkHMnZGu2wNrMEmGBjBpUh3vqEY1L3jaMw==",
"license": "Apache-2.0" "license": "Apache-2.0"
}, },
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz",
"integrity": "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==",
"cpu": [
"ppc64"
],
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"peer": true,
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.25.11.tgz",
"integrity": "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"peer": true,
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.25.11.tgz",
"integrity": "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"peer": true,
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.25.11.tgz",
"integrity": "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"peer": true,
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.11.tgz",
"integrity": "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"peer": true,
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.25.11.tgz",
"integrity": "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"peer": true,
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.11.tgz",
"integrity": "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"peer": true,
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.11.tgz",
"integrity": "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"peer": true,
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.25.11.tgz",
"integrity": "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.25.11.tgz",
"integrity": "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.25.11.tgz",
"integrity": "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==",
"cpu": [
"ia32"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.25.11.tgz",
"integrity": "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==",
"cpu": [
"loong64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.11.tgz",
"integrity": "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==",
"cpu": [
"mips64el"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.11.tgz",
"integrity": "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==",
"cpu": [
"ppc64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.11.tgz",
"integrity": "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==",
"cpu": [
"riscv64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.25.11.tgz",
"integrity": "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==",
"cpu": [
"s390x"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.25.11.tgz",
"integrity": "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.11.tgz",
"integrity": "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"peer": true,
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.11.tgz",
"integrity": "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"peer": true,
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.11.tgz",
"integrity": "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"peer": true,
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.11.tgz",
"integrity": "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"peer": true,
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openharmony-arm64": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.11.tgz",
"integrity": "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"peer": true,
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.25.11.tgz",
"integrity": "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"peer": true,
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.25.11.tgz",
"integrity": "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.25.11.tgz",
"integrity": "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==",
"cpu": [
"ia32"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.11.tgz",
"integrity": "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">=18"
}
},
"node_modules/@rollup/plugin-virtual": {
"version": "3.0.2",
"resolved": "https://registry.npmmirror.com/@rollup/plugin-virtual/-/plugin-virtual-3.0.2.tgz",
"integrity": "sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A==",
"license": "MIT",
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.52.5",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz",
"integrity": "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"peer": true
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.52.5",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.5.tgz",
"integrity": "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"peer": true
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.52.5",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.5.tgz",
"integrity": "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"peer": true
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.52.5",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.5.tgz",
"integrity": "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"peer": true
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.52.5",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.5.tgz",
"integrity": "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"peer": true
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.52.5",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.5.tgz",
"integrity": "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"peer": true
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.52.5",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.5.tgz",
"integrity": "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.52.5",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.5.tgz",
"integrity": "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.52.5",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.5.tgz",
"integrity": "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.52.5",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.5.tgz",
"integrity": "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true
},
"node_modules/@rollup/rollup-linux-loong64-gnu": {
"version": "4.52.5",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.5.tgz",
"integrity": "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==",
"cpu": [
"loong64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.52.5",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.5.tgz",
"integrity": "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==",
"cpu": [
"ppc64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.52.5",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.5.tgz",
"integrity": "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==",
"cpu": [
"riscv64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.52.5",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.5.tgz",
"integrity": "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==",
"cpu": [
"riscv64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.52.5",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.5.tgz",
"integrity": "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==",
"cpu": [
"s390x"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.52.5",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.5.tgz",
"integrity": "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.52.5",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.5.tgz",
"integrity": "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true
},
"node_modules/@rollup/rollup-openharmony-arm64": {
"version": "4.52.5",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.5.tgz",
"integrity": "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"peer": true
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.52.5",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.5.tgz",
"integrity": "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"peer": true
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.52.5",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.5.tgz",
"integrity": "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==",
"cpu": [
"ia32"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"peer": true
},
"node_modules/@rollup/rollup-win32-x64-gnu": {
"version": "4.52.5",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.5.tgz",
"integrity": "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"peer": true
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.52.5",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.5.tgz",
"integrity": "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"peer": true
},
"node_modules/@swc/core": {
"version": "1.14.0",
"resolved": "https://registry.npmmirror.com/@swc/core/-/core-1.14.0.tgz",
"integrity": "sha512-oExhY90bes5pDTVrei0xlMVosTxwd/NMafIpqsC4dMbRYZ5KB981l/CX8tMnGsagTplj/RcG9BeRYmV6/J5m3w==",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@swc/counter": "^0.1.3",
"@swc/types": "^0.1.25"
},
"engines": {
"node": ">=10"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/swc"
},
"optionalDependencies": {
"@swc/core-darwin-arm64": "1.14.0",
"@swc/core-darwin-x64": "1.14.0",
"@swc/core-linux-arm-gnueabihf": "1.14.0",
"@swc/core-linux-arm64-gnu": "1.14.0",
"@swc/core-linux-arm64-musl": "1.14.0",
"@swc/core-linux-x64-gnu": "1.14.0",
"@swc/core-linux-x64-musl": "1.14.0",
"@swc/core-win32-arm64-msvc": "1.14.0",
"@swc/core-win32-ia32-msvc": "1.14.0",
"@swc/core-win32-x64-msvc": "1.14.0"
},
"peerDependencies": {
"@swc/helpers": ">=0.5.17"
},
"peerDependenciesMeta": {
"@swc/helpers": {
"optional": true
}
}
},
"node_modules/@swc/core-darwin-arm64": {
"version": "1.14.0",
"resolved": "https://registry.npmmirror.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.14.0.tgz",
"integrity": "sha512-uHPC8rlCt04nvYNczWzKVdgnRhxCa3ndKTBBbBpResOZsRmiwRAvByIGh599j+Oo6Z5eyTPrgY+XfJzVmXnN7Q==",
"cpu": [
"arm64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-darwin-x64": {
"version": "1.14.0",
"resolved": "https://registry.npmmirror.com/@swc/core-darwin-x64/-/core-darwin-x64-1.14.0.tgz",
"integrity": "sha512-2SHrlpl68vtePRknv9shvM9YKKg7B9T13tcTg9aFCwR318QTYo+FzsKGmQSv9ox/Ua0Q2/5y2BNjieffJoo4nA==",
"cpu": [
"x64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-arm-gnueabihf": {
"version": "1.14.0",
"resolved": "https://registry.npmmirror.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.14.0.tgz",
"integrity": "sha512-SMH8zn01dxt809svetnxpeg/jWdpi6dqHKO3Eb11u4OzU2PK7I5uKS6gf2hx5LlTbcJMFKULZiVwjlQLe8eqtg==",
"cpu": [
"arm"
],
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-arm64-gnu": {
"version": "1.14.0",
"resolved": "https://registry.npmmirror.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.14.0.tgz",
"integrity": "sha512-q2JRu2D8LVqGeHkmpVCljVNltG0tB4o4eYg+dElFwCS8l2Mnt9qurMCxIeo9mgoqz0ax+k7jWtIRHktnVCbjvQ==",
"cpu": [
"arm64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-arm64-musl": {
"version": "1.14.0",
"resolved": "https://registry.npmmirror.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.14.0.tgz",
"integrity": "sha512-uofpVoPCEUjYIv454ZEZ3sLgMD17nIwlz2z7bsn7rl301Kt/01umFA7MscUovFfAK2IRGck6XB+uulMu6aFhKQ==",
"cpu": [
"arm64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-x64-gnu": {
"version": "1.14.0",
"resolved": "https://registry.npmmirror.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.14.0.tgz",
"integrity": "sha512-quTTx1Olm05fBfv66DEBuOsOgqdypnZ/1Bh3yGXWY7ANLFeeRpCDZpljD9BSjdsNdPOlwJmEUZXMHtGm3v1TZQ==",
"cpu": [
"x64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-x64-musl": {
"version": "1.14.0",
"resolved": "https://registry.npmmirror.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.14.0.tgz",
"integrity": "sha512-caaNAu+aIqT8seLtCf08i8C3/UC5ttQujUjejhMcuS1/LoCKtNiUs4VekJd2UGt+pyuuSrQ6dKl8CbCfWvWeXw==",
"cpu": [
"x64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-win32-arm64-msvc": {
"version": "1.14.0",
"resolved": "https://registry.npmmirror.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.14.0.tgz",
"integrity": "sha512-EeW3jFlT3YNckJ6V/JnTfGcX7UHGyh6/AiCPopZ1HNaGiXVCKHPpVQZicmtyr/UpqxCXLrTgjHOvyMke7YN26A==",
"cpu": [
"arm64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-win32-ia32-msvc": {
"version": "1.14.0",
"resolved": "https://registry.npmmirror.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.14.0.tgz",
"integrity": "sha512-dPai3KUIcihV5hfoO4QNQF5HAaw8+2bT7dvi8E5zLtecW2SfL3mUZipzampXq5FHll0RSCLzlrXnSx+dBRZIIQ==",
"cpu": [
"ia32"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-win32-x64-msvc": {
"version": "1.14.0",
"resolved": "https://registry.npmmirror.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.14.0.tgz",
"integrity": "sha512-nm+JajGrTqUA6sEHdghDlHMNfH1WKSiuvljhdmBACW4ta4LC3gKurX2qZuiBARvPkephW9V/i5S8QPY1PzFEqg==",
"cpu": [
"x64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/counter": {
"version": "0.1.3",
"resolved": "https://registry.npmmirror.com/@swc/counter/-/counter-0.1.3.tgz",
"integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==",
"license": "Apache-2.0"
},
"node_modules/@swc/types": {
"version": "0.1.25",
"resolved": "https://registry.npmmirror.com/@swc/types/-/types-0.1.25.tgz",
"integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==",
"license": "Apache-2.0",
"dependencies": {
"@swc/counter": "^0.1.3"
}
},
"node_modules/@swc/wasm": {
"version": "1.14.0",
"resolved": "https://registry.npmmirror.com/@swc/wasm/-/wasm-1.14.0.tgz",
"integrity": "sha512-eUUA3jEwFzoMh6mUksvaqX3wK+FxKFFFapBic+sQigxD5NytXM+PFARbv17BECKV/XcCCROHFV9+e73lYOsV3A==",
"license": "Apache-2.0"
},
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"license": "MIT",
"peer": true
},
"node_modules/@uqrcode/js": { "node_modules/@uqrcode/js": {
"version": "4.0.7", "version": "4.0.7",
"resolved": "https://registry.npmmirror.com/@uqrcode/js/-/js-4.0.7.tgz", "resolved": "https://registry.npmmirror.com/@uqrcode/js/-/js-4.0.7.tgz",
...@@ -119,7 +1114,7 @@ ...@@ -119,7 +1114,7 @@
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-2.0.6.tgz", "resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-2.0.6.tgz",
"integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==",
"dev": true, "devOptional": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"is-what": "^3.14.1" "is-what": "^3.14.1"
...@@ -190,6 +1185,66 @@ ...@@ -190,6 +1185,66 @@
"errno": "cli.js" "errno": "cli.js"
} }
}, },
"node_modules/esbuild": {
"version": "0.25.11",
"resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.11.tgz",
"integrity": "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==",
"hasInstallScript": true,
"license": "MIT",
"peer": true,
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.25.11",
"@esbuild/android-arm": "0.25.11",
"@esbuild/android-arm64": "0.25.11",
"@esbuild/android-x64": "0.25.11",
"@esbuild/darwin-arm64": "0.25.11",
"@esbuild/darwin-x64": "0.25.11",
"@esbuild/freebsd-arm64": "0.25.11",
"@esbuild/freebsd-x64": "0.25.11",
"@esbuild/linux-arm": "0.25.11",
"@esbuild/linux-arm64": "0.25.11",
"@esbuild/linux-ia32": "0.25.11",
"@esbuild/linux-loong64": "0.25.11",
"@esbuild/linux-mips64el": "0.25.11",
"@esbuild/linux-ppc64": "0.25.11",
"@esbuild/linux-riscv64": "0.25.11",
"@esbuild/linux-s390x": "0.25.11",
"@esbuild/linux-x64": "0.25.11",
"@esbuild/netbsd-arm64": "0.25.11",
"@esbuild/netbsd-x64": "0.25.11",
"@esbuild/openbsd-arm64": "0.25.11",
"@esbuild/openbsd-x64": "0.25.11",
"@esbuild/openharmony-arm64": "0.25.11",
"@esbuild/sunos-x64": "0.25.11",
"@esbuild/win32-arm64": "0.25.11",
"@esbuild/win32-ia32": "0.25.11",
"@esbuild/win32-x64": "0.25.11"
}
},
"node_modules/fdir": {
"version": "6.5.0",
"resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/find-up": { "node_modules/find-up": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz", "resolved": "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz",
...@@ -202,6 +1257,21 @@ ...@@ -202,6 +1257,21 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"peer": true,
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/get-caller-file": { "node_modules/get-caller-file": {
"version": "2.0.5", "version": "2.0.5",
"resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz", "resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz",
...@@ -270,7 +1340,7 @@ ...@@ -270,7 +1340,7 @@
"version": "3.14.1", "version": "3.14.1",
"resolved": "https://registry.npmmirror.com/is-what/-/is-what-3.14.1.tgz", "resolved": "https://registry.npmmirror.com/is-what/-/is-what-3.14.1.tgz",
"integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==",
"dev": true, "devOptional": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/js-sha256": { "node_modules/js-sha256": {
...@@ -283,7 +1353,7 @@ ...@@ -283,7 +1353,7 @@
"version": "4.3.0", "version": "4.3.0",
"resolved": "https://registry.npmmirror.com/less/-/less-4.3.0.tgz", "resolved": "https://registry.npmmirror.com/less/-/less-4.3.0.tgz",
"integrity": "sha512-X9RyH9fvemArzfdP8Pi3irr7lor2Ok4rOttDXBhlwDg+wKQsXOXgHWduAJE1EsF7JJx0w0bcO6BC6tCKKYnXKA==", "integrity": "sha512-X9RyH9fvemArzfdP8Pi3irr7lor2Ok4rOttDXBhlwDg+wKQsXOXgHWduAJE1EsF7JJx0w0bcO6BC6tCKKYnXKA==",
"dev": true, "devOptional": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"copy-anything": "^2.0.1", "copy-anything": "^2.0.1",
...@@ -424,7 +1494,7 @@ ...@@ -424,7 +1494,7 @@
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmmirror.com/parse-node-version/-/parse-node-version-1.0.1.tgz", "resolved": "https://registry.npmmirror.com/parse-node-version/-/parse-node-version-1.0.1.tgz",
"integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==",
"dev": true, "devOptional": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">= 0.10" "node": ">= 0.10"
...@@ -449,6 +1519,40 @@ ...@@ -449,6 +1519,40 @@
"node": ">= 0.4.1" "node": ">= 0.4.1"
} }
}, },
"node_modules/pdfjs-dist": {
"version": "2.11.338",
"resolved": "https://registry.npmmirror.com/pdfjs-dist/-/pdfjs-dist-2.11.338.tgz",
"integrity": "sha512-Ti5VTB0VvSdtTtc7TG71ghMx0SEuNcEs4ghVuZxW0p6OqLjMc0xekZV1B+MmlxEG2Du2e5jgazucWIG/SXTcdA==",
"license": "Apache-2.0",
"peerDependencies": {
"worker-loader": "^3.0.8"
},
"peerDependenciesMeta": {
"worker-loader": {
"optional": true
}
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"license": "ISC",
"peer": true
},
"node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pify": { "node_modules/pify": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmmirror.com/pify/-/pify-4.0.1.tgz", "resolved": "https://registry.npmmirror.com/pify/-/pify-4.0.1.tgz",
...@@ -468,6 +1572,54 @@ ...@@ -468,6 +1572,54 @@
"node": ">=10.13.0" "node": ">=10.13.0"
} }
}, },
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/postcss/node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"peer": true,
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/prr": { "node_modules/prr": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmmirror.com/prr/-/prr-1.0.1.tgz", "resolved": "https://registry.npmmirror.com/prr/-/prr-1.0.1.tgz",
...@@ -510,6 +1662,48 @@ ...@@ -510,6 +1662,48 @@
"resolved": "https://registry.npmmirror.com/require-main-filename/-/require-main-filename-2.0.0.tgz", "resolved": "https://registry.npmmirror.com/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
}, },
"node_modules/rollup": {
"version": "4.52.5",
"resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.52.5.tgz",
"integrity": "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/estree": "1.0.8"
},
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=18.0.0",
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.52.5",
"@rollup/rollup-android-arm64": "4.52.5",
"@rollup/rollup-darwin-arm64": "4.52.5",
"@rollup/rollup-darwin-x64": "4.52.5",
"@rollup/rollup-freebsd-arm64": "4.52.5",
"@rollup/rollup-freebsd-x64": "4.52.5",
"@rollup/rollup-linux-arm-gnueabihf": "4.52.5",
"@rollup/rollup-linux-arm-musleabihf": "4.52.5",
"@rollup/rollup-linux-arm64-gnu": "4.52.5",
"@rollup/rollup-linux-arm64-musl": "4.52.5",
"@rollup/rollup-linux-loong64-gnu": "4.52.5",
"@rollup/rollup-linux-ppc64-gnu": "4.52.5",
"@rollup/rollup-linux-riscv64-gnu": "4.52.5",
"@rollup/rollup-linux-riscv64-musl": "4.52.5",
"@rollup/rollup-linux-s390x-gnu": "4.52.5",
"@rollup/rollup-linux-x64-gnu": "4.52.5",
"@rollup/rollup-linux-x64-musl": "4.52.5",
"@rollup/rollup-openharmony-arm64": "4.52.5",
"@rollup/rollup-win32-arm64-msvc": "4.52.5",
"@rollup/rollup-win32-ia32-msvc": "4.52.5",
"@rollup/rollup-win32-x64-gnu": "4.52.5",
"@rollup/rollup-win32-x64-msvc": "4.52.5",
"fsevents": "~2.3.2"
}
},
"node_modules/safer-buffer": { "node_modules/safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz",
...@@ -553,6 +1747,16 @@ ...@@ -553,6 +1747,16 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"license": "BSD-3-Clause",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/string-width": { "node_modules/string-width": {
"version": "4.2.3", "version": "4.2.3",
"resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
...@@ -585,6 +1789,23 @@ ...@@ -585,6 +1789,23 @@
"utrie": "^1.0.2" "utrie": "^1.0.2"
} }
}, },
"node_modules/tinyglobby": {
"version": "0.2.15",
"resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz",
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"fdir": "^6.5.0",
"picomatch": "^4.0.3"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/tslib": { "node_modules/tslib": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz", "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
...@@ -604,6 +1825,109 @@ ...@@ -604,6 +1825,109 @@
"base64-arraybuffer": "^1.0.2" "base64-arraybuffer": "^1.0.2"
} }
}, },
"node_modules/uuid": {
"version": "10.0.0",
"resolved": "https://registry.npmmirror.com/uuid/-/uuid-10.0.0.tgz",
"integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"license": "MIT",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/vite": {
"version": "7.1.12",
"resolved": "https://registry.npmmirror.com/vite/-/vite-7.1.12.tgz",
"integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==",
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.5.0",
"picomatch": "^4.0.3",
"postcss": "^8.5.6",
"rollup": "^4.43.0",
"tinyglobby": "^0.2.15"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
},
"optionalDependencies": {
"fsevents": "~2.3.3"
},
"peerDependencies": {
"@types/node": "^20.19.0 || >=22.12.0",
"jiti": ">=1.21.0",
"less": "^4.0.0",
"lightningcss": "^1.21.0",
"sass": "^1.70.0",
"sass-embedded": "^1.70.0",
"stylus": ">=0.54.8",
"sugarss": "^5.0.0",
"terser": "^5.16.0",
"tsx": "^4.8.1",
"yaml": "^2.4.2"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
},
"jiti": {
"optional": true
},
"less": {
"optional": true
},
"lightningcss": {
"optional": true
},
"sass": {
"optional": true
},
"sass-embedded": {
"optional": true
},
"stylus": {
"optional": true
},
"sugarss": {
"optional": true
},
"terser": {
"optional": true
},
"tsx": {
"optional": true
},
"yaml": {
"optional": true
}
}
},
"node_modules/vite-plugin-top-level-await": {
"version": "1.6.0",
"resolved": "https://registry.npmmirror.com/vite-plugin-top-level-await/-/vite-plugin-top-level-await-1.6.0.tgz",
"integrity": "sha512-bNhUreLamTIkoulCR9aDXbTbhLk6n1YE8NJUTTxl5RYskNRtzOR0ASzSjBVRtNdjIfngDXo11qOsybGLNsrdww==",
"license": "MIT",
"dependencies": {
"@rollup/plugin-virtual": "^3.0.2",
"@swc/core": "^1.12.14",
"@swc/wasm": "^1.12.14",
"uuid": "10.0.0"
},
"peerDependencies": {
"vite": ">=2.8"
}
},
"node_modules/which-module": { "node_modules/which-module": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmmirror.com/which-module/-/which-module-2.0.1.tgz", "resolved": "https://registry.npmmirror.com/which-module/-/which-module-2.0.1.tgz",
......
...@@ -25,9 +25,11 @@ ...@@ -25,9 +25,11 @@
"mp-painter": "^1.0.1", "mp-painter": "^1.0.1",
"nanoid": "^4.0.0", "nanoid": "^4.0.0",
"pdf.js": "^0.1.0", "pdf.js": "^0.1.0",
"pdfjs-dist": "^2.11.338",
"qrcode": "^1.5.4", "qrcode": "^1.5.4",
"qrcodejs2": "^0.0.2", "qrcodejs2": "^0.0.2",
"uqrcodejs": "^4.0.7" "uqrcodejs": "^4.0.7",
"vite-plugin-top-level-await": "^1.6.0"
}, },
"devDependencies": { "devDependencies": {
"less": "^4.3.0" "less": "^4.3.0"
......
...@@ -480,6 +480,7 @@ ...@@ -480,6 +480,7 @@
"style" : "style" :
{ {
"navigationBarTitleText" : "", "navigationBarTitleText" : "",
"disableScroll": false,
"webview": { "webview": {
// 1. 允许加载本地静态资源(viewer.html /static 目录) // 1. 允许加载本地静态资源(viewer.html /static 目录)
"allowAccessFromFileURLs": true, "allowAccessFromFileURLs": true,
...@@ -588,7 +589,7 @@ ...@@ -588,7 +589,7 @@
},{ },{
"path": "product-list/product-commission", "path": "product-list/product-commission",
"style": { "style": {
"navigationBarTitleText": "持牌人佣金" "navigationBarTitleText": "产品佣率查询"
} }
}, },
{ {
......
...@@ -7,20 +7,32 @@ ...@@ -7,20 +7,32 @@
<view class="spinner"></view> <view class="spinner"></view>
<text>加载中...</text> <text>加载中...</text>
</view> </view>
<!-- 1. 公司介绍(直接显示单个PDF) --> <!-- 1. 公司介绍(直接显示单个PDF) -->
<view v-if="!loading && currentType === 1 && companyPdf.url" class="pdf-single"> <view v-if="!loading && currentType === 1 && companyPdf.urls" class="pdf-single">
<web-view <!-- 当前Tab的PDF列表 -->
:src="getPdfViewerUrl(companyPdf.url)" <view
class="webview" v-for="(url, index) in currentPdf.urls"
@error="handlePdfError" :key="index"
@load="handleWebViewLoad" class="pdf-item-container"
></web-view> >
<PdfViewer
:pdfInfo="{
title: null,
url: url
}"
:autoLoad="index === 0"
:lazyLoad="true"
:maxRetryCount="2"
@loadComplete="handlePdfLoadComplete"
@loadError="handlePdfLoadError"
@pageChange="handlePageChange"
/>
</view>
</view> </view>
<!-- 2-4. 带Tab的模块(根据type显示不同Tab,且仅显示有权限的Tab) --> <!-- 2-4. 带Tab的模块 -->
<view v-if="!loading && currentType >= 2 && currentType <= 4"> <view v-if="!loading && currentType >= 2 && currentType <= 4">
<!-- Tab切换栏(仅渲染有权限的Tab) --> <!-- Tab切换栏 -->
<view class="tab-bar"> <view class="tab-bar">
<view <view
v-for="(tab, index) in filteredCurrentTabs" v-for="(tab, index) in filteredCurrentTabs"
...@@ -34,29 +46,39 @@ ...@@ -34,29 +46,39 @@
</view> </view>
</view> </view>
<!-- Tab对应的PDF内容(无权限时显示空状态) --> <!-- Tab对应的PDF内容 -->
<view class="pdf-tab-content" v-if="currentPdf.type==='showURL'"> <view class="pdf-tab-content" v-if="currentPdf.type === 'showURL'">
<template v-if="filteredCurrentTabs.length > 0"> <template v-if="filteredCurrentTabs.length > 0 && currentPdf.urls">
<view class="pdfContainer"> <!-- 当前Tab的PDF列表 -->
<image :src="currentPdf.url" class="pdf-image"></image> <view
</view> v-for="(url, index) in currentPdf.urls"
:key="index"
<!-- <web-view class="pdf-item-container"
:src="getPdfViewerUrl(currentPdf.url)" >
class="webview" <PdfViewer
@error="handlePdfError" :pdfInfo="{
@load="handleWebViewLoad" title: null,
:key="getPdfViewerUrl(currentPdf.url)" url: url,
></web-view> --> landscapeFlag:currentPdf.landscapeFlag
}"
:autoLoad="index === 0"
:lazyLoad="true"
:maxRetryCount="2"
@loadComplete="handlePdfLoadComplete"
@loadError="handlePdfLoadError"
@pageChange="handlePageChange"
/>
</view>
</template> </template>
<template v-else> <template v-else>
<view class="pdf-empty">暂无访问权限</view> <view class="pdf-empty">暂无访问权限</view>
</template> </template>
</view> </view>
<!-- 显示产品搜索组件 -->
<view class="pdf-tab-content" v-if="currentPdf.type==='showPage'"> <!-- 显示产品搜索组件 -->
<productListVue/> <view class="pdf-tab-content" v-if="currentPdf.type === 'showPage'">
</view> <productListVue/>
</view>
</view> </view>
<!-- 错误状态 --> <!-- 错误状态 -->
...@@ -68,95 +90,161 @@ ...@@ -68,95 +90,161 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed } from 'vue'; import { ref, computed, watch } from 'vue';
import { onLoad } from '@dcloudio/uni-app'; import { onLoad } from '@dcloudio/uni-app';
import api from "@/api/api"; import api from "@/api/api";
import productListVue from '@/myPackageA/product-list/product-list.vue'; import productListVue from '@/myPackageA/product-list/product-list.vue';
// 导入PDF查看器组件
import PdfViewer from '@/components/pdf-viewer/pdf-viewer.vue';
// ========================== 类型定义 ========================== // ========================== 类型定义 ==========================
// PDF文件类型
interface PdfItem { interface PdfItem {
id: string; id: string;
title: string; title: string;
url: string; // PDF文件路径 urls: string[];
type:string; type: string;
landscapeFlag?:boolean;
} }
// 权限数据类型(与接口返回结构对齐)
interface PermissionRight { interface PermissionRight {
key: string; key: string;
} }
interface PermissionItem { interface PermissionItem {
key: string; // 对应菜单的key(如cases、products) key: string;
rights: PermissionRight[]; // 子权限(对应Tab的id) rights: PermissionRight[];
} }
// ========================== 基础配置 ========================== // ========================== 响应式数据 ==========================
// OSS基础路径(根据实际OSS地址修改)
const OSS_BASE_URL = 'https://csf-doc-center.oss-cn-shanghai-finance-1-pub.aliyuncs.com'; const OSS_BASE_URL = 'https://csf-doc-center.oss-cn-shanghai-finance-1-pub.aliyuncs.com';
// 接收URL参数的type(默认1:公司介绍)
const currentType = ref<number>(1); const currentType = ref<number>(1);
// 加载状态
const loading = ref(true); const loading = ref(true);
// 当前激活的Tab索引
const activeTab = ref(0); const activeTab = ref(0);
// 当前显示的PDF信息 const currentPdf = ref<PdfItem>({ id: '', title: '', urls: [], type: '' ,landscapeFlag:false});
const currentPdf = ref<PdfItem>({ id: '', title: '', url: '', type:''});
// 接口返回的权限列表
const permissionList = ref<PermissionItem[]>([]); const permissionList = ref<PermissionItem[]>([]);
// 公司介绍PDF(type=1,固定配置) // 公司介绍PDF
const companyPdf = ref<PdfItem>({ const companyPdf = ref<PdfItem>({
id: 'company', id: 'company',
title: '公司介绍', title: '公司介绍',
url: `${OSS_BASE_URL}/public/company-intro.pdf`, // urls: Array.from({ length: 21 }, (_, i) =>
type:'showURL' // `${OSS_BASE_URL}/public/company-intro_part${i + 1}.pdf`
// ),
urls: [`${OSS_BASE_URL}/public/company-intro.pdf`],
type: 'showURL'
}); });
// 所有模块的原始Tab配置(未过滤权限) // 其他配置保持不变...
const rawTabConfig = ref<Record<number, PdfItem[]>>({ const rawTabConfig = ref<Record<number, PdfItem[]>>({
2: [ // type=2:案例分享(对应权限key=cases) 2: [
{ id: 'case1', title: '规划案例', url: `${OSS_BASE_URL}/cases/planning.pdf`,type:'showURL' }, {
{ id: 'case2', title: '团财案例', url: `${OSS_BASE_URL}/cases/CorporateServiceDeliveryCase-GroupandPropertyInsurance.pdf`,type:'showURL' } id: 'case1',
title: '规划案例',
urls: [
`${OSS_BASE_URL}/cases/planning.pdf`,
],
type: 'showURL' ,
},
{
id: 'case2',
title: '团财案例',
urls: [
`${OSS_BASE_URL}/cases/CorporateServiceDeliveryCase-GroupandPropertyInsurance.pdf`
],
type: 'showURL' ,
}
], ],
3: [ // type=3:产品分析(对应权限key=products) 3: [
{ id: 'product2', title: '港险分析', url: `${OSS_BASE_URL}/products/hk-analysis.pdf`,type:'showURL' }, {
{ id: 'product1', title: '港险明细', url: `${OSS_BASE_URL}/products/2025Q3-hk-detail.pdf`,type:'showURL' }, id: 'product2',
{ id: 'product3', title: '产品对比', url: '',type:'showPage' }, title: '港险分析',
urls: [
`${OSS_BASE_URL}/products/hk-analysis-20251031.pdf`,
],
type: 'showURL' ,
landscapeFlag:true
},
{
id: 'product1',
title: '港险明细',
urls: [
`${OSS_BASE_URL}/products/2025Q3-hk-detail.pdf`,
],
type: 'showURL' ,
landscapeFlag:true
},
{
id: 'product3',
title: '产品对比',
urls: [],
type: 'showPage'
},
], ],
4: [ // type=4:制度(对应权限key=policies) 4: [
{ id: 'policy1', title: '个险政策', url: `${OSS_BASE_URL}/policies/individual-policy.pdf`,type:'showURL' }, {
{ id: 'policy2', title: '家办政策', url: `${OSS_BASE_URL}/policies/group-policy.pdf`,type:'showURL' }, id: 'policy1',
{ id: 'policy3', title: '介绍费政策', url: `${OSS_BASE_URL}/policies/commission-policy.pdf`,type:'showURL' } title: '个险政策',
urls: [
`${OSS_BASE_URL}/policies/individual-policy.pdf`
],
type: 'showURL'
},
{
id: 'policy2',
title: '家办政策',
urls: [
`${OSS_BASE_URL}/policies/group-policy.pdf`,
],
type: 'showURL'
},
{
id: 'policy3',
title: '介绍费政策',
urls: [
`${OSS_BASE_URL}/policies/commission-policy.pdf`
],
type: 'showURL'
}
] ]
}); });
// 类型与权限key的映射关系(关键:建立type和权限key的关联)
const typeToPermissionKey = ref<Record<number, string>>({ const typeToPermissionKey = ref<Record<number, string>>({
2: 'cases', // type=2(案例分享)→ 权限key=cases 2: 'cases',
3: 'products', // type=3(产品分析)→ 权限key=products 3: 'products',
4: 'policies' // type=4(制度)→ 权限key=policies 4: 'policies'
}); });
// ========================== 生命周期 ========================== // ========================== 事件处理 ==========================
onLoad((query) => { const handlePdfLoadComplete = (url: string, pageCount: number) => {
// 1. 解析URL中的type参数(默认1) console.log(`PDF加载完成: ${url}, 共 ${pageCount} 页`);
currentType.value = Number(query.type) || 1; };
const index = uni.getStorageSync('tabsIndex');
setTimeout(()=>{ const handlePdfLoadError = (url: string, error: Error) => {
if(index){ console.error(`PDF加载失败: ${url}`, error);
switchTab(index) uni.showToast({
} title: 'PDF加载失败',
},300) icon: 'none',
duration: 2000
// 2. 查询用户信息和权限 });
queryUserInfoAndPermission(); };
});
const handlePageChange = (currentPage: number, totalPages: number) => {
console.log(`页面变化: ${currentPage}/${totalPages}`);
};
// ========================== 辅助方法 ==========================
const getPdfTitle = (url: string): string => {
if (!url) return '未命名文件';
const fileName = url.split('/').pop() || '';
const title = fileName
.replace(/\.pdf$/i, '')
.replace(/[-_]/g, ' ')
.replace(/([A-Z])/g, ' $1')
.trim();
return title || 'PDF文档';
};
// ========================== 核心逻辑 ========================== // ========================== 核心方法(简化版) ==========================
/**
* 查询用户信息和权限列表
*/
const queryUserInfoAndPermission = () => { const queryUserInfoAndPermission = () => {
const userId = uni.getStorageSync('cffp_userId'); const userId = uni.getStorageSync('cffp_userId');
if (!userId) { if (!userId) {
...@@ -167,13 +255,11 @@ const queryUserInfoAndPermission = () => { ...@@ -167,13 +255,11 @@ const queryUserInfoAndPermission = () => {
api.queryInfo({ userId }).then(res => { api.queryInfo({ userId }).then(res => {
if (res.success) { if (res.success) {
// 存储接口返回的权限列表(格式:[{key: 'cases', rights: [{key: 'case1'}]}])
permissionList.value = res.data.accessPermission || []; permissionList.value = res.data.accessPermission || [];
} else { } else {
uni.setStorageSync('loginType', 'visitor'); uni.setStorageSync('loginType', 'visitor');
permissionList.value = []; // 游客无权限 permissionList.value = [];
} }
// 权限获取后初始化PDF显示
initPdfAfterPermission(); initPdfAfterPermission();
}).catch(() => { }).catch(() => {
uni.setStorageSync('loginType', 'visitor'); uni.setStorageSync('loginType', 'visitor');
...@@ -182,149 +268,82 @@ const queryUserInfoAndPermission = () => { ...@@ -182,149 +268,82 @@ const queryUserInfoAndPermission = () => {
}); });
}; };
/**
* 权限获取后初始化PDF(过滤Tab + 设置默认显示项)
*/
const initPdfAfterPermission = () => { const initPdfAfterPermission = () => {
loading.value = true; loading.value = true;
// 1. 过滤当前type对应的有权限的Tab
const filteredTabs = filteredCurrentTabs.value; const filteredTabs = filteredCurrentTabs.value;
// 2. 处理不同type的初始化逻辑
if (currentType.value >= 2 && currentType.value <= 4) { if (currentType.value >= 2 && currentType.value <= 4) {
if (filteredTabs.length > 0) { if (filteredTabs.length > 0) {
// 有有权限的Tab:默认显示第一个
activeTab.value = 0; activeTab.value = 0;
currentPdf.value = filteredTabs[0]; currentPdf.value = filteredTabs[0];
} else { } else {
// 无权限:清空当前PDF currentPdf.value = { id: '', title: '', urls: [], type: '',landscapeFlag:false };
currentPdf.value = { id: '', title: '', url: '',type:'' };
} }
}else{
currentPdf.value = { id: 'company', title: '公司介绍', urls: [`${OSS_BASE_URL}/public/company-intro.pdf`], type: 'showURL',landscapeFlag:false };
} }
// 3. 模拟加载延迟(优化用户体验)
setTimeout(() => { setTimeout(() => {
loading.value = false; loading.value = false;
}, 200); }, 200);
}; };
/**
* 计算属性:过滤当前type对应的有权限的Tab列表
*/
const filteredCurrentTabs = computed(() => { const filteredCurrentTabs = computed(() => {
// 1. 获取当前type的原始Tab列表
const originalTabs = rawTabConfig.value[currentType.value] || []; const originalTabs = rawTabConfig.value[currentType.value] || [];
// 2. 若为游客/无权限列表:返回空
if (permissionList.value.length === 0) return []; if (permissionList.value.length === 0) return [];
// 3. 获取当前type对应的权限key(如type=2→key=cases)
const targetPermissionKey = typeToPermissionKey.value[currentType.value]; const targetPermissionKey = typeToPermissionKey.value[currentType.value];
if (!targetPermissionKey) return []; if (!targetPermissionKey) return [];
// 4. 找到权限列表中对应的权限项
const targetPermission = permissionList.value.find( const targetPermission = permissionList.value.find(
item => item.key === targetPermissionKey item => item.key === targetPermissionKey
); );
if (!targetPermission || targetPermission.rights.length === 0) return []; if (!targetPermission || targetPermission.rights.length === 0) return [];
// 5. 提取有权限的Tab id集合(如['case1', 'case2'])
const allowedTabIds = new Set( const allowedTabIds = new Set(
targetPermission.rights.map(right => right.key) targetPermission.rights.map(right => right.key)
); );
// 6. 过滤原始Tab列表:仅保留有权限的项
return originalTabs.filter(tab => allowedTabIds.has(tab.id)); return originalTabs.filter(tab => allowedTabIds.has(tab.id));
}); });
/**
* 切换Tab
* @param index 目标Tab索引
*/
const switchTab = (index: number) => { const switchTab = (index: number) => {
const tabs = filteredCurrentTabs.value; const tabs = filteredCurrentTabs.value;
// 边界校验:索引无效或无权限Tab时不处理
if (index < 0 || index >= tabs.length || tabs.length === 0) return; if (index < 0 || index >= tabs.length || tabs.length === 0) return;
// 显示加载状态(优化体验) uni.showLoading({ title: '切换中...' });
uni.showLoading({ title: '加载中...' });
// 延迟100ms更新:避免WebView加载冲突
setTimeout(() => { setTimeout(() => {
activeTab.value = index; activeTab.value = index;
currentPdf.value = tabs[index]; currentPdf.value = tabs[index];
uni.setStorageSync('tabsIndex', index); uni.setStorageSync('tabsIndex', index);
// 延迟关闭加载提示(给WebView启动时间)
setTimeout(() => { setTimeout(() => {
uni.hideLoading(); uni.hideLoading();
}, 300); }, 300);
}, 100); }, 100);
}; };
/** // ========================== 生命周期 ==========================
* 生成PDF查看器URL(添加随机参数+时间戳,避免缓存) onLoad((query) => {
* @param pdfUrl PDF原始URL currentType.value = Number(query.type) || 1;
*/ const index = uni.getStorageSync('tabsIndex');
const getPdfViewerUrl = (pdfUrl: string) => { setTimeout(() => {
if (!pdfUrl) return ''; if (index) {
const randomStr = getRandomStr(); // 随机字符串 switchTab(index);
const timestamp = new Date().getTime(); // 时间戳 }
// 编码PDF URL + 拼接唯一参数 }, 300);
return `/static/pdf/web/viewer.html?file=${encodeURIComponent(pdfUrl)}&random=${randomStr}&t=${timestamp}`;
}; queryUserInfoAndPermission();
});
/**
* 生成指定长度的随机字符串(用于URL唯一标识)
* @param length 字符串长度(默认8)
*/
const getRandomStr = (length = 8) => {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let str = '';
for (let i = 0; i < length; i++) {
str += chars.charAt(Math.floor(Math.random() * chars.length));
}
return str;
};
// ========================== 事件处理 ==========================
/**
* WebView加载完成监听(调试用)
*/
const handleWebViewLoad = (e: any) => {
const loadedSrc = e.detail.src;
console.log('WebView加载完成:', loadedSrc);
// 解析并验证PDF地址(可选:调试时用)
const urlObj = new URL(loadedSrc);
const fileParam = urlObj.searchParams.get('file');
if (fileParam) {
const decodedFileUrl = decodeURIComponent(fileParam);
console.log('当前加载的PDF地址:', decodedFileUrl);
// 可选:验证PDF地址可用性
uni.request({
url: decodedFileUrl,
method: 'GET',
success: (res) => {
console.log('PDF地址可访问,状态码:', res.statusCode);
},
fail: (err) => {
console.error('PDF地址访问失败:', err);
uni.showToast({ title: `PDF地址无效: ${err.errMsg}`, icon: 'none' });
}
});
}
};
/** // 监听当前PDF变化
* PDF加载错误处理 watch(() => currentPdf.value, (newVal) => {
*/ // Tab切换时不需要额外处理,组件会自动处理
const handlePdfError = () => { });
uni.showToast({
title: 'PDF加载失败,请重试',
icon: 'none',
duration: 2000
});
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
/* 样式可以大幅简化,因为PDF查看逻辑都在组件中 */
.module-container { .module-container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
...@@ -339,7 +358,6 @@ const handlePdfError = () => { ...@@ -339,7 +358,6 @@ const handlePdfError = () => {
position: relative; position: relative;
} }
/* 加载状态 */
.loading { .loading {
flex: 1; flex: 1;
display: flex; display: flex;
...@@ -364,7 +382,6 @@ const handlePdfError = () => { ...@@ -364,7 +382,6 @@ const handlePdfError = () => {
} }
} }
/* 错误状态 */
.error { .error {
flex: 1; flex: 1;
display: flex; display: flex;
...@@ -375,15 +392,14 @@ const handlePdfError = () => { ...@@ -375,15 +392,14 @@ const handlePdfError = () => {
background-color: #ffffff; background-color: #ffffff;
} }
/* 单个PDF展示(公司介绍) */ .pdf-item-container {
.pdf-single { margin-bottom: 24rpx;
flex: 1; background: #ffffff;
width: 100%; border-radius: 8rpx;
height: 100%; overflow: hidden;
background-color: #ffffff; box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
} }
/* Tab栏样式 */
.tab-bar { .tab-bar {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
...@@ -392,10 +408,10 @@ const handlePdfError = () => { ...@@ -392,10 +408,10 @@ const handlePdfError = () => {
border-bottom: 1px solid #eeeeee; border-bottom: 1px solid #eeeeee;
overflow-x: auto; overflow-x: auto;
white-space: nowrap; white-space: nowrap;
scrollbar-width: none; /* 隐藏滚动条(火狐) */ scrollbar-width: none;
&::-webkit-scrollbar { &::-webkit-scrollbar {
display: none; /* 隐藏滚动条(Chrome/Safari) */ display: none;
} }
.tab-item { .tab-item {
...@@ -433,30 +449,17 @@ const handlePdfError = () => { ...@@ -433,30 +449,17 @@ const handlePdfError = () => {
} }
} }
/* Tab对应的PDF内容区域 */
.pdf-tab-content { .pdf-tab-content {
width: 100%; width: 100%;
position: absolute; position: absolute;
top: 48px; /* 与Tab栏高度一致,避免重叠 */ top: 48px;
bottom: 0; bottom: 0;
background-color: #ffffff; background-color: #f5f5f5;
} overflow-y: auto;
.pdfContainer{ padding: 16rpx;
display: flex; box-sizing: border-box;
width: 100%;
height: 100%;
align-items: center;
justify-content: center;
position: relative;
margin: 0 auto 8px auto;
}
.pdfContainer .pdf-image{
width: 100%;
height: 100%;
position: relative;
z-index:100
} }
/* 无权限/无Tab时的空状态 */
.pdf-empty { .pdf-empty {
width: 100%; width: 100%;
height: 100%; height: 100%;
...@@ -468,13 +471,6 @@ const handlePdfError = () => { ...@@ -468,13 +471,6 @@ const handlePdfError = () => {
background-color: #ffffff; background-color: #ffffff;
} }
/* WebView样式(占满父容器) */
.webview {
width: 100%;
height: 100%;
}
/* 加载动画 */
@keyframes spin { @keyframes spin {
to { to {
transform: rotate(360deg); transform: rotate(360deg);
......
...@@ -224,7 +224,7 @@ ...@@ -224,7 +224,7 @@
{key:'policies',title:'制度',icon:'icon-xiaoshoue',link:'/pages/personalCenter/detail',isOpen:true,isShow:false,identity: true,type:4}, {key:'policies',title:'制度',icon:'icon-xiaoshoue',link:'/pages/personalCenter/detail',isOpen:true,isShow:false,identity: true,type:4},
{key:'products',title:'产品分析',icon:'icon-shujufenxi',link:'/pages/personalCenter/detail',isOpen:true,isShow:false,identity: true,type:3}, {key:'products',title:'产品分析',icon:'icon-shujufenxi',link:'/pages/personalCenter/detail',isOpen:true,isShow:false,identity: true,type:3},
{key:'cases',title:'案例分享',icon:'icon-shiyongjiaocheng',link:'/pages/personalCenter/detail',isOpen:true,isShow:false,identity: true,type:2}, {key:'cases',title:'案例分享',icon:'icon-shiyongjiaocheng',link:'/pages/personalCenter/detail',isOpen:true,isShow:false,identity: true,type:2},
{key:'commission',title:'持牌人佣金',icon:'icon-shiyongjiaocheng',link:'/myPackageA/product-list/product-commission',isOpen:true,isShow:false,identity: true}, {key:'commission',title:'持牌人佣金',icon:'icon-dingdan',link:'/myPackageA/product-list/product-commission',isOpen:true,isShow:false,identity: true},
], ],
},{id:'04',categoryName:'海外资产配置', },{id:'04',categoryName:'海外资产配置',
children:[ children:[
......
import { getDocument, GlobalWorkerOptions } from 'pdfjs-dist/legacy/build/pdf';
// 使用 CDN 避免本地 worker 问题
GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js`;
export const pdfToImage = async (pdfUrl) => {
const loadingTask = getDocument(pdfUrl);
const pdf = await loadingTask.promise;
let pages = [];
for (let num = 1; num <= pdf.numPages; num++) {
const page = await pdf.getPage(num);
const viewport = page.getViewport({ scale: 1 });
const canvas = document.createElement('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;
pages.push(canvas.toDataURL('image/png'));
}
return pages;
}
\ No newline at end of file
/**
* 创建水印
* @param text 水印文本
*/
export function createWatermark(text: string | string[] = '内部资料') {
// 检查是否在浏览器环境
if (typeof document === 'undefined') {
console.warn('Watermark: 非浏览器环境,无法创建水印');
return;
}
console.log('Watermark: 开始创建水印', text);
try {
// 创建Canvas生成水印图片
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
if (!ctx) {
throw new Error('无法获取Canvas上下文');
}
// 设置Canvas尺寸
canvas.width = 300;
canvas.height = 200;
// 设置样式
ctx.font = 'normal 16px Arial, sans-serif';
ctx.fillStyle = 'rgba(0, 0, 0, 0.06)';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
// 旋转画布
ctx.translate(150, 100);
ctx.rotate(-25 * Math.PI / 180);
ctx.translate(-150, -100);
// 绘制文本
const texts = Array.isArray(text) ? text : [text];
const lineHeight = 24;
const startY = (200 - (texts.length - 1) * lineHeight) / 2;
texts.forEach((textItem, index) => {
ctx.fillText(textItem, 150, startY + index * lineHeight);
});
// 转换为DataURL
const dataURL = canvas.toDataURL('image/png');
// 创建水印元素
const watermark = document.createElement('div');
watermark.style.position = 'fixed';
watermark.style.top = '0';
watermark.style.left = '0';
watermark.style.width = '100%';
watermark.style.height = '100%';
watermark.style.backgroundImage = `url(${dataURL})`;
watermark.style.backgroundRepeat = 'repeat';
watermark.style.backgroundSize = '400px 300px';
watermark.style.pointerEvents = 'none';
watermark.style.zIndex = '9999';
watermark.style.opacity = '0.8';
// 添加到body
document.body.appendChild(watermark);
console.log('Watermark: 水印创建成功');
// 返回销毁方法
return () => {
if (watermark.parentNode) {
watermark.parentNode.removeChild(watermark);
console.log('Watermark: 水印已销毁');
}
};
} catch (error) {
console.error('Watermark: 创建水印失败', error);
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment