feat(screenshots): serve thumbnails while keeping original for preview and copy
This commit is contained in:
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user