修复详情页账号截图加载
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeUnmount, ref, watch } from 'vue'
|
||||
import { fetchAdminFileBlob, fetchFileBlobByURL } from '@/shared/api/files'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
source: string
|
||||
alt?: string
|
||||
admin?: boolean
|
||||
imageClass?: string
|
||||
fallbackClass?: string
|
||||
loading?: 'eager' | 'lazy'
|
||||
decoding?: 'async' | 'sync' | 'auto'
|
||||
}>(),
|
||||
{
|
||||
alt: '',
|
||||
admin: false,
|
||||
imageClass: '',
|
||||
fallbackClass: '',
|
||||
loading: 'lazy',
|
||||
decoding: 'async',
|
||||
}
|
||||
)
|
||||
|
||||
const imageURL = ref('')
|
||||
const failed = ref(false)
|
||||
let createdObjectURL = ''
|
||||
|
||||
const fallbackText = computed(() => (failed.value ? '图片加载失败' : '图片加载中'))
|
||||
|
||||
function extractObjectKey(value: string) {
|
||||
try {
|
||||
const parsed = new URL(value, window.location.origin)
|
||||
return parsed.searchParams.get('key') || ''
|
||||
} catch {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
function shouldFetchWithAuth(value: string) {
|
||||
return value.includes('/api/files/object') || value.includes('/api/admin/files/object')
|
||||
}
|
||||
|
||||
function revokeCurrentURL() {
|
||||
if (!createdObjectURL) return
|
||||
URL.revokeObjectURL(createdObjectURL)
|
||||
createdObjectURL = ''
|
||||
}
|
||||
|
||||
async function loadImage() {
|
||||
revokeCurrentURL()
|
||||
imageURL.value = ''
|
||||
failed.value = false
|
||||
|
||||
if (!props.source) {
|
||||
failed.value = true
|
||||
return
|
||||
}
|
||||
|
||||
if (!shouldFetchWithAuth(props.source)) {
|
||||
imageURL.value = props.source
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const key = extractObjectKey(props.source)
|
||||
const blob =
|
||||
props.admin && key ? await fetchAdminFileBlob(key) : await fetchFileBlobByURL(props.source)
|
||||
createdObjectURL = URL.createObjectURL(blob)
|
||||
imageURL.value = createdObjectURL
|
||||
} catch {
|
||||
failed.value = true
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => [props.source, props.admin] as const, loadImage, { immediate: true })
|
||||
|
||||
onBeforeUnmount(revokeCurrentURL)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<img
|
||||
v-if="imageURL"
|
||||
:class="imageClass"
|
||||
:src="imageURL"
|
||||
:alt="alt"
|
||||
:loading="loading"
|
||||
:decoding="decoding"
|
||||
/>
|
||||
<span v-else :class="['auth-image-fallback', fallbackClass]">{{ fallbackText }}</span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.auth-image-fallback {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 120px;
|
||||
border: 1px dashed #d8dee8;
|
||||
border-radius: 12px;
|
||||
background: #f8fafc;
|
||||
color: #94a3b8;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user