优化文件加载速度,采用懒加载方法
This commit is contained in:
246
src/main.js
246
src/main.js
@@ -401,7 +401,7 @@ function setupOtherEventListeners() {
|
||||
}
|
||||
|
||||
// ============ 加载动画控制 ============
|
||||
function showLoading(text = '正在分析文件...', subtext = '生成缩略图中,请稍候') {
|
||||
function showLoading(text = '正在分析文件...', subtext = '') {
|
||||
if (elements.loadingOverlay) {
|
||||
const textEl = elements.loadingOverlay.querySelector('.loading-text');
|
||||
const subtextEl = elements.loadingOverlay.querySelector('.loading-subtext');
|
||||
@@ -426,8 +426,8 @@ async function handleFilePaths(paths) {
|
||||
|
||||
console.log('处理文件:', paths);
|
||||
|
||||
// 显示加载动画
|
||||
showLoading(`正在分析 ${paths.length} 个文件...`, '生成缩略图中,请稍候');
|
||||
// 显示加载动画(不再提示生成缩略图)
|
||||
showLoading(`正在分析 ${paths.length} 个文件...`);
|
||||
|
||||
try {
|
||||
console.log('调用 analyze_files...');
|
||||
@@ -442,6 +442,10 @@ async function handleFilePaths(paths) {
|
||||
groupFilesByType();
|
||||
renderFileTypeCards();
|
||||
switchPage('config-page');
|
||||
|
||||
// 异步懒加载编码信息和图片(不阻塞 UI)
|
||||
lazyLoadCodecInfo();
|
||||
lazyLoadImages();
|
||||
} catch (error) {
|
||||
console.error('分析文件失败:', error);
|
||||
alert('分析文件失败: ' + (error.message || error));
|
||||
@@ -671,12 +675,20 @@ function renderPreviews(files, type) {
|
||||
};
|
||||
|
||||
return files.map(file => {
|
||||
if (file.thumbnail) {
|
||||
return `<div class="preview-item"><img src="${file.thumbnail}" alt="${file.name}" loading="lazy"></div>`;
|
||||
// 对于图片类型,显示占位符,然后异步加载
|
||||
if (type === 'image' && file.path) {
|
||||
// 如果已经有缓存的图片数据,直接使用
|
||||
if (file.imageData) {
|
||||
return `<div class="preview-item" data-file-id="${file.id}"><img src="${file.imageData}" alt="${file.name}" loading="lazy"></div>`;
|
||||
}
|
||||
// 否则显示占位符,稍后异步加载
|
||||
const icon = previewIcons.image;
|
||||
return `<div class="preview-item image" data-file-id="${file.id}" data-loading="true">${icon}</div>`;
|
||||
}
|
||||
// 根据类型显示不同的 SVG 图标
|
||||
|
||||
// 视频和音频:只显示图标,不生成缩略图
|
||||
const icon = previewIcons[type] || previewIcons.image;
|
||||
return `<div class="preview-item ${type}">${icon}</div>`;
|
||||
return `<div class="preview-item ${type}" data-file-id="${file.id}">${icon}</div>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
@@ -1162,49 +1174,29 @@ function openFilesModal(fileType) {
|
||||
image: `<svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.5"><rect width="18" height="18" x="3" y="3" rx="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"/></svg>`,
|
||||
};
|
||||
|
||||
// 生成编码参数显示文本
|
||||
function getCodecInfoText(file) {
|
||||
if (!file.codec_info) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
const info = file.codec_info;
|
||||
const parts = [];
|
||||
|
||||
if (fileType === 'video') {
|
||||
// 视频文件显示:分辨率 + 视频编码 + 帧率
|
||||
if (info.resolution) parts.push(info.resolution);
|
||||
if (info.video_codec) parts.push(info.video_codec);
|
||||
if (info.frame_rate) parts.push(info.frame_rate);
|
||||
if (info.duration) parts.push(info.duration);
|
||||
} else if (fileType === 'audio') {
|
||||
// 音频文件显示:音频编码 + 采样率 + 声道
|
||||
if (info.audio_codec) parts.push(info.audio_codec);
|
||||
if (info.sample_rate) parts.push(info.sample_rate);
|
||||
if (info.channels) parts.push(info.channels);
|
||||
if (info.duration) parts.push(info.duration);
|
||||
} else {
|
||||
// 图片等其他类型
|
||||
if (info.resolution) parts.push(info.resolution);
|
||||
}
|
||||
|
||||
return parts.length > 0 ? parts.join(' · ') : '-';
|
||||
}
|
||||
|
||||
elements.filesList.innerHTML = files.map(file => {
|
||||
let thumbnail = '';
|
||||
if (file.thumbnail) {
|
||||
thumbnail = `<img src="${file.thumbnail}" alt="${file.name}" loading="lazy">`;
|
||||
|
||||
// 对于图片类型,使用缓存的图片数据或显示占位符
|
||||
if (fileType === 'image' && file.path) {
|
||||
if (file.imageData) {
|
||||
thumbnail = `<img src="${file.imageData}" alt="${file.name}" loading="lazy">`;
|
||||
} else {
|
||||
// 显示占位符
|
||||
const icon = fileListIcons.image;
|
||||
thumbnail = `<span class="file-list-icon ${fileType}">${icon}</span>`;
|
||||
}
|
||||
} else {
|
||||
// 默认 SVG 图标
|
||||
// 视频和音频:只显示图标,不生成缩略图
|
||||
const icon = fileListIcons[fileType] || fileListIcons.image;
|
||||
thumbnail = `<span class="file-list-icon ${fileType}">${icon}</span>`;
|
||||
}
|
||||
|
||||
const codecInfo = getCodecInfoText(file);
|
||||
// 编码信息显示(如果还没加载,显示加载中)
|
||||
const codecInfo = file.codec_info ? getCodecInfoTextForFile(file) : '加载中...';
|
||||
|
||||
return `
|
||||
<div class="file-list-item" data-type="${fileType}">
|
||||
<div class="file-list-item" data-type="${fileType}" data-file-id="${file.id}">
|
||||
<div class="file-col-thumb">
|
||||
<div class="file-list-thumbnail">${thumbnail}</div>
|
||||
</div>
|
||||
@@ -1225,6 +1217,9 @@ function openFilesModal(fileType) {
|
||||
}).join('');
|
||||
|
||||
elements.filesModal.classList.add('active');
|
||||
|
||||
// 打开弹窗后,异步加载还没有编码信息的文件
|
||||
loadCodecInfoForFiles(files);
|
||||
}
|
||||
|
||||
function closeFilesModal() {
|
||||
@@ -1635,3 +1630,172 @@ async function loadTemplates() {
|
||||
console.error('加载模板失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// ============ 缩略图功能已移除 ============
|
||||
// 视频和音频文件不再生成缩略图,只显示图标
|
||||
// 图片文件直接显示原图,无需生成缩略图
|
||||
// 这样可以大幅提升加载速度,特别是处理大量文件时
|
||||
|
||||
// ============ 懒加载编码信息 ============
|
||||
async function lazyLoadCodecInfo() {
|
||||
console.log('开始懒加载编码信息...');
|
||||
|
||||
// 需要获取编码信息的文件
|
||||
const filesToLoad = state.files.filter(file =>
|
||||
!file.codec_info && (file.file_type === 'video' || file.file_type === 'audio' || file.file_type === 'image')
|
||||
);
|
||||
|
||||
if (filesToLoad.length === 0) {
|
||||
console.log('没有需要加载的编码信息');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`需要加载 ${filesToLoad.length} 个文件的编码信息`);
|
||||
|
||||
// 并发加载编码信息(限制并发数为 5,比缩略图可以更多)
|
||||
const concurrency = 5;
|
||||
for (let i = 0; i < filesToLoad.length; i += concurrency) {
|
||||
const batch = filesToLoad.slice(i, i + concurrency);
|
||||
await Promise.all(batch.map(file => loadSingleCodecInfo(file)));
|
||||
}
|
||||
|
||||
console.log('所有编码信息加载完成');
|
||||
}
|
||||
|
||||
async function loadSingleCodecInfo(file) {
|
||||
try {
|
||||
const codecInfo = await window.tauriInvoke('get_file_info', {
|
||||
path: file.path,
|
||||
fileType: file.file_type
|
||||
});
|
||||
|
||||
// 更新文件对象
|
||||
file.codec_info = codecInfo;
|
||||
|
||||
// 更新 DOM 显示(如果文件列表弹窗已打开)
|
||||
updateCodecInfoInDOM(file);
|
||||
|
||||
console.log(`✅ 编码信息加载成功: ${file.name}`);
|
||||
} catch (error) {
|
||||
console.warn(`⚠️ 编码信息加载失败: ${file.name}`, error);
|
||||
}
|
||||
}
|
||||
|
||||
function updateCodecInfoInDOM(file) {
|
||||
// 查找文件列表中对应的元素
|
||||
const fileListItems = document.querySelectorAll(`.file-list-item[data-file-id="${file.id}"]`);
|
||||
fileListItems.forEach(item => {
|
||||
const codecEl = item.querySelector('.file-list-codec');
|
||||
if (codecEl && file.codec_info) {
|
||||
const codecInfo = getCodecInfoTextForFile(file);
|
||||
codecEl.textContent = codecInfo;
|
||||
codecEl.title = codecInfo;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 为指定的文件列表加载编码信息
|
||||
async function loadCodecInfoForFiles(files) {
|
||||
const filesToLoad = files.filter(file => !file.codec_info);
|
||||
|
||||
if (filesToLoad.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`为文件列表加载 ${filesToLoad.length} 个编码信息`);
|
||||
|
||||
// 并发加载
|
||||
const concurrency = 5;
|
||||
for (let i = 0; i < filesToLoad.length; i += concurrency) {
|
||||
const batch = filesToLoad.slice(i, i + concurrency);
|
||||
await Promise.all(batch.map(file => loadSingleCodecInfo(file)));
|
||||
}
|
||||
}
|
||||
|
||||
// ============ 懒加载图片 ============
|
||||
async function lazyLoadImages() {
|
||||
console.log('开始懒加载图片...');
|
||||
|
||||
// 需要加载的图片文件
|
||||
const imagesToLoad = state.files.filter(file =>
|
||||
file.file_type === 'image' && !file.imageData
|
||||
);
|
||||
|
||||
if (imagesToLoad.length === 0) {
|
||||
console.log('没有需要加载的图片');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`需要加载 ${imagesToLoad.length} 个图片`);
|
||||
|
||||
// 并发加载图片(限制并发数为 3)
|
||||
const concurrency = 3;
|
||||
for (let i = 0; i < imagesToLoad.length; i += concurrency) {
|
||||
const batch = imagesToLoad.slice(i, i + concurrency);
|
||||
await Promise.all(batch.map(file => loadSingleImage(file)));
|
||||
}
|
||||
|
||||
console.log('所有图片加载完成');
|
||||
}
|
||||
|
||||
async function loadSingleImage(file) {
|
||||
try {
|
||||
const imageData = await window.tauriInvoke('read_image_as_base64', {
|
||||
path: file.path
|
||||
});
|
||||
|
||||
// 缓存图片数据
|
||||
file.imageData = imageData;
|
||||
|
||||
// 更新 DOM 显示
|
||||
updateImageInDOM(file.id, imageData);
|
||||
|
||||
console.log(`✅ 图片加载成功: ${file.name}`);
|
||||
} catch (error) {
|
||||
console.warn(`⚠️ 图片加载失败: ${file.name}`, error);
|
||||
}
|
||||
}
|
||||
|
||||
function updateImageInDOM(fileId, imageData) {
|
||||
// 更新预览网格中的图片
|
||||
const previewItems = document.querySelectorAll(`.preview-item[data-file-id="${fileId}"]`);
|
||||
previewItems.forEach(item => {
|
||||
if (item.dataset.loading === 'true') {
|
||||
item.innerHTML = `<img src="${imageData}" alt="" loading="lazy">`;
|
||||
item.dataset.loading = 'false';
|
||||
item.classList.remove('image');
|
||||
}
|
||||
});
|
||||
|
||||
// 更新文件列表弹窗中的图片
|
||||
const fileListItems = document.querySelectorAll(`.file-list-item[data-file-id="${fileId}"] .file-list-thumbnail`);
|
||||
fileListItems.forEach(item => {
|
||||
item.innerHTML = `<img src="${imageData}" alt="" loading="lazy">`;
|
||||
});
|
||||
}
|
||||
|
||||
function getCodecInfoTextForFile(file) {
|
||||
if (!file.codec_info) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
const info = file.codec_info;
|
||||
const parts = [];
|
||||
const fileType = file.file_type;
|
||||
|
||||
if (fileType === 'video') {
|
||||
if (info.resolution) parts.push(info.resolution);
|
||||
if (info.video_codec) parts.push(info.video_codec);
|
||||
if (info.frame_rate) parts.push(info.frame_rate);
|
||||
if (info.duration) parts.push(info.duration);
|
||||
} else if (fileType === 'audio') {
|
||||
if (info.audio_codec) parts.push(info.audio_codec);
|
||||
if (info.sample_rate) parts.push(info.sample_rate);
|
||||
if (info.channels) parts.push(info.channels);
|
||||
if (info.duration) parts.push(info.duration);
|
||||
} else if (fileType === 'image') {
|
||||
if (info.resolution) parts.push(info.resolution);
|
||||
}
|
||||
|
||||
return parts.length > 0 ? parts.join(' · ') : '-';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user