feat(screenshots): serve thumbnails while keeping original for preview and copy

This commit is contained in:
2026-02-07 11:02:16 +08:00
parent 2d5be0feb2
commit 21c537da10
17 changed files with 130 additions and 54 deletions

View File

@@ -15,6 +15,10 @@ function buildUrl(filename) {
return `/screenshots/${encodeURIComponent(filename)}`
}
function buildThumbUrl(filename) {
return `/screenshots/thumb/${encodeURIComponent(filename)}`
}
async function load() {
loading.value = true
try {
@@ -34,13 +38,13 @@ function openPreview(item) {
previewOpen.value = true
}
function findRenderedShotImage(filename) {
try {
const escaped = typeof CSS !== 'undefined' && typeof CSS.escape === 'function' ? CSS.escape(String(filename)) : String(filename)
return document.querySelector(`img[data-shot-filename="${escaped}"]`)
} catch {
return null
}
function onThumbError(event, item) {
const imageEl = event?.target
if (!imageEl) return
if (imageEl.dataset.fullLoaded === '1') return
imageEl.dataset.fullLoaded = '1'
imageEl.src = buildUrl(item.filename)
}
function canvasToPngBlob(canvas) {
@@ -96,17 +100,8 @@ async function blobToPng(blob) {
}
}
async function screenshotUrlToPngBlob(url, filename) {
// 优先使用页面上已渲染完成的 <img>(避免额外请求;也更容易满足剪贴板“用户手势”限制)
const imgEl = findRenderedShotImage(filename)
if (imgEl) {
try {
return await imageElementToPngBlob(imgEl)
} catch {
// fallback to fetch
}
}
async function screenshotUrlToPngBlob(url) {
// 复制时始终拉取原图,避免复制到缩略图
const resp = await fetch(url, { credentials: 'include', cache: 'no-store' })
if (!resp.ok) throw new Error('fetch_failed')
const blob = await resp.blob()
@@ -183,11 +178,11 @@ async function copyImage(item) {
try {
await navigator.clipboard.write([
new ClipboardItem({
'image/png': screenshotUrlToPngBlob(url, item.filename),
'image/png': screenshotUrlToPngBlob(url),
}),
])
} catch {
const pngBlob = await screenshotUrlToPngBlob(url, item.filename)
const pngBlob = await screenshotUrlToPngBlob(url)
await navigator.clipboard.write([new ClipboardItem({ 'image/png': pngBlob })])
}
ElMessage.success('图片已复制到剪贴板')
@@ -235,10 +230,10 @@ onMounted(load)
<el-card v-for="item in screenshots" :key="item.filename" shadow="never" class="shot-card" :body-style="{ padding: '0' }">
<img
class="shot-img"
:src="buildUrl(item.filename)"
:src="buildThumbUrl(item.filename)"
:alt="item.display_name || item.filename"
:data-shot-filename="item.filename"
loading="lazy"
@error="onThumbError($event, item)"
@click="openPreview(item)"
/>
<div class="shot-body">