Files
admin-web/src/modules/inbounds/measurement/components/MeasurementEvidenceThumbnail.vue
T
2026-08-17 18:00:42 +08:00

80 lines
2.6 KiB
Vue

<script setup lang="ts">
import { onBeforeUnmount, ref, watch } from 'vue'
import { ImageOff } from '@lucide/vue'
import { downloadWeightThumbnail } from '../../weight/api/weightOcr'
const props = withDefaults(defineProps<{
customerId: string
inboundId: string
fileId?: string | null
alt: string
emptyText?: string
}>(), {
fileId: null,
emptyText: '未绑定测量图片',
})
defineEmits<{ open: [] }>()
const imageUrl = ref('')
const loading = ref(false)
const failed = ref(false)
let requestId = 0
function releaseImage() {
if (imageUrl.value) URL.revokeObjectURL(imageUrl.value)
imageUrl.value = ''
}
async function load() {
const currentRequest = ++requestId
releaseImage()
failed.value = false
if (!props.fileId) {
loading.value = false
return
}
loading.value = true
try {
const blob = await downloadWeightThumbnail(props.customerId, props.inboundId, props.fileId)
if (currentRequest !== requestId) return
imageUrl.value = URL.createObjectURL(blob)
} catch {
if (currentRequest === requestId) failed.value = true
} finally {
if (currentRequest === requestId) loading.value = false
}
}
watch(() => [props.customerId, props.inboundId, props.fileId], load, { immediate: true })
onBeforeUnmount(() => {
requestId += 1
releaseImage()
})
</script>
<template>
<button
class="measurement-evidence-thumb"
type="button"
:disabled="!fileId"
:aria-label="fileId ? `查看${alt}` : emptyText"
@click="$emit('open')"
>
<img v-if="imageUrl" :src="imageUrl" :alt="alt" loading="lazy" />
<span v-else class="measurement-evidence-thumb__state" :class="{ 'is-loading': loading }">
<ImageOff v-if="failed || !fileId" :size="22" aria-hidden="true" />
<span>{{ !fileId ? emptyText : failed ? '图片加载失败' : '图片加载中' }}</span>
</span>
</button>
</template>
<style scoped>
.measurement-evidence-thumb { display: block; width: 100%; min-width: 96px; aspect-ratio: 4 / 3; overflow: hidden; padding: 0; border: 1px solid var(--cc-border); border-radius: 6px; background: #f2f4f7; cursor: zoom-in; }
.measurement-evidence-thumb:disabled { cursor: default; }
.measurement-evidence-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
.measurement-evidence-thumb__state { display: grid; width: 100%; height: 100%; place-items: center; align-content: center; gap: 7px; padding: 8px; color: #667085; font-size: 12px; }
.measurement-evidence-thumb__state.is-loading { color: #98a2b3; }
.measurement-evidence-thumb:focus-visible { outline: 2px solid var(--cc-primary); outline-offset: 2px; }
</style>