Files
FileHost/app/components/ImageUploader.vue
T
O96u bc4cba7e52
Publish Docker image / build-and-push (push) Canceled after 0s
ci / ci (22, ubuntu-latest) (push) Canceled after 0s
release: PicHost v1.0.0
Multi-user image hosting with RBAC, upload preferences, and admin settings.
2026-08-22 14:36:57 +08:00

141 lines
3.9 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
const props = defineProps<{
disabled?: boolean
}>()
const emit = defineEmits<{
'upload': [files: File[]]
'open-settings': []
}>()
const fileInput = ref<HTMLInputElement | null>(null)
const dragging = ref(false)
const hovering = ref(false)
const { bindPaste } = useClipboardImage(ref(null))
const isActive = computed(() => dragging.value || hovering.value)
bindPaste((files) => {
if (!props.disabled) emit('upload', files)
})
function openFilePicker() {
if (props.disabled) return
fileInput.value?.click()
}
function onFileChange(event: Event) {
const input = event.target as HTMLInputElement
const files = input.files ? Array.from(input.files) : []
if (files.length) emit('upload', files)
input.value = ''
}
function onDrop(event: DragEvent) {
event.preventDefault()
dragging.value = false
if (props.disabled) return
const files = Array.from(event.dataTransfer?.files ?? [])
.filter(file => file.type.startsWith('image/'))
if (files.length) emit('upload', files)
}
function onDragOver(event: DragEvent) {
event.preventDefault()
if (!props.disabled) dragging.value = true
}
function onDragLeave() {
dragging.value = false
}
function onMouseEnter() {
if (!props.disabled) hovering.value = true
}
function onMouseLeave() {
hovering.value = false
}
function openSettings() {
if (props.disabled) return
emit('open-settings')
}
</script>
<template>
<div
class="upload-card-surface group/upload relative text-center transition-all duration-300 ease-out"
:class="isActive
? 'scale-[1.01] border-primary bg-primary/5 shadow-md shadow-primary/10'
: 'hover:border-primary/50 hover:bg-primary/[0.04] hover:shadow-sm'"
@drop="onDrop"
@dragover="onDragOver"
@dragleave="onDragLeave"
@mouseenter="onMouseEnter"
@mouseleave="onMouseLeave"
>
<input
ref="fileInput"
type="file"
accept="image/jpeg,image/png,image/webp,image/gif,image/svg+xml,image/x-icon,.svg,.ico"
multiple
class="hidden"
:disabled="disabled"
@change="onFileChange"
>
<div class="absolute top-3 right-3 z-10">
<button
type="button"
class="upload-settings-btn inline-flex size-8 items-center justify-center rounded-md text-muted transition-colors hover:bg-elevated hover:text-highlighted disabled:pointer-events-none disabled:opacity-40"
aria-label="上传设置"
:disabled="disabled"
@click="openSettings"
>
<UIcon
name="i-lucide-settings"
class="upload-settings-icon size-4"
/>
</button>
</div>
<div class="mx-auto max-w-lg space-y-4">
<div
class="mx-auto flex size-16 items-center justify-center rounded-2xl bg-primary/10 transition-transform duration-300 ease-out"
:class="isActive ? 'scale-110' : 'group-hover/upload:scale-105'"
>
<UIcon
name="i-lucide-cloud-upload"
class="size-8 text-primary transition-transform duration-300"
:class="isActive ? '-translate-y-0.5' : ''"
/>
</div>
<div class="space-y-2">
<p class="text-xl font-semibold tracking-tight">
拖拽图片到此处上传
</p>
<p class="text-sm text-muted">
或点击选择文件,按
<kbd class="mx-1 rounded border border-default bg-muted/50 px-1.5 py-0.5 font-mono text-xs">Ctrl</kbd>
+
<kbd class="mx-1 rounded border border-default bg-muted/50 px-1.5 py-0.5 font-mono text-xs">V</kbd>
粘贴图片
</p>
</div>
<p class="text-xs text-dimmed">
支持 JPEG / PNG / WebP / GIF / SVG / ICO,单张最大 10 MB,每次最多 10 张
</p>
<UButton
label="选择图片"
icon="i-lucide-upload"
size="lg"
:disabled="disabled"
@click="openFilePicker"
/>
</div>
</div>
</template>