Files
pj_admin_cloud/src/components/FileImage/index.vue
T
dzc d86bc810cc fix:图片访问
fix:权限修改
2026-10-29 14:26:56 +08:00

73 lines
2.5 KiB
Vue

<script setup lang="ts">
import { shallowRef, watch } from 'vue'
import { ElImage } from 'element-plus'
import { accessFileUrl, fileUrls, originalFileUrl, resolveFileUrl, fileAccessRevision, retryFileAccess } from '/@/utils/file-access'
defineOptions({ inheritAttrs: false })
const props = withDefaults(defineProps<{ src?: string; previewSrcList?: string[] }>(), {
src: '', previewSrcList: () => [],
})
const emit = defineEmits<{
load: [event: Event]
error: [event: Event]
show: []
switch: [index: number]
close: []
}>()
const displaySrc = shallowRef('')
const previewSources = shallowRef<string[]>([])
let generation = 0
let loaded = false
let failed = false
let pending = false
let lastError: Event
const identity = (source: string) => resolveFileUrl(originalFileUrl(source))
const refreshPreview = () => { previewSources.value = fileUrls(props.previewSrcList) }
// An already decoded image remains visible after its request signature expires.
// Only a changed business reference or a failed request needs a new thumbnail request.
watch(() => identity(props.src), () => {
generation++
loaded = failed = pending = false
displaySrc.value = accessFileUrl(props.src)
}, { immediate: true })
watch(() => JSON.stringify(props.previewSrcList.map(identity)), refreshPreview, { immediate: true })
function onLoad(event: Event) { loaded = true; failed = false; emit('load', event) }
async function onError(event: Event) {
failed = true
lastError = event
if (pending) return
const current = generation
pending = true
try {
const retried = await retryFileAccess(props.src)
if (current !== generation) return
if (retried) {
failed = false
displaySrc.value = accessFileUrl(props.src)
} else emit('error', event)
} finally { if (current === generation) pending = false }
}
watch(fileAccessRevision, () => {
if (loaded || pending) return
const renewed = accessFileUrl(props.src)
if (renewed !== displaySrc.value) {
failed = false
displaySrc.value = renewed
} else if (failed) void onError(lastError)
})
function onShow() { refreshPreview(); emit('show') }
function onSwitch(index: number) { refreshPreview(); emit('switch', index) }
</script>
<template>
<ElImage v-bind="$attrs" :src="displaySrc" :preview-src-list="previewSources"
@load="onLoad" @error="onError" @show="onShow" @switch="onSwitch" @close="emit('close')">
<template v-for="(_, name) in $slots" #[name]="scope">
<slot :name="name" v-bind="scope || {}" />
</template>
</ElImage>
</template>