file-upload
Avatar upload
Avatar upload
Загрузка превью…
src/file-upload/c-file-upload-2.vue
<!-- Иконка по умолчанию (нет файла) — путь lucide "user". -->
<script setup lang="ts">
import { computed } from "vue"
import { Button } from "@/components/ui/button"
import { formatBytes, useFileUpload } from "@/composables/use-file-upload"
import ErrorAlert from "./_shared/ErrorAlert.vue"
const maxSize = 2 * 1024 * 1024
const [state, { removeFile, handleDragEnter, handleDragLeave, handleDragOver, handleDrop, openFileDialog, handleFileChange }] = useFileUpload({
maxFiles: 1,
maxSize,
accept: "image/*",
multiple: false,
})
const currentFile = computed(() => state.files[0])
</script>
<template>
<div class="flex flex-col items-center gap-4">
<div class="relative">
<div
class="group/avatar relative h-24 w-24 cursor-pointer overflow-hidden rounded-full border border-dashed transition-colors"
:class="[
state.isDragging ? 'border-primary bg-primary/5' : 'border-muted-foreground/25 hover:border-muted-foreground/20',
currentFile?.preview && 'border-solid',
]"
@dragenter="handleDragEnter"
@dragleave="handleDragLeave"
@dragover="handleDragOver"
@drop="handleDrop"
@click="openFileDialog"
>
<input type="file" accept="image/*" class="sr-only" @change="handleFileChange" />
<img v-if="currentFile?.preview" :src="currentFile.preview" alt="Avatar" class="h-full w-full object-cover" />
<div v-else class="flex h-full w-full items-center justify-center">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="text-muted-foreground size-6"
><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" /><circle cx="12" cy="7" r="4" /></svg>
</div>
</div>
<Button
v-if="currentFile"
size="icon"
variant="outline"
class="absolute end-0.5 top-0.5 z-10 size-6 rounded-full dark:bg-zinc-800 hover:dark:bg-zinc-700"
aria-label="Remove avatar"
@click="removeFile(currentFile.id)"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-3.5"
><path d="M18 6 6 18" /><path d="m6 6 12 12" /></svg>
</Button>
</div>
<div class="space-y-0.5 text-center">
<p class="text-sm font-medium">{{ currentFile ? "Avatar uploaded" : "Upload avatar" }}</p>
<p class="text-muted-foreground text-xs">{{ `PNG, JPG up to ${formatBytes(maxSize)}` }}</p>
</div>
<ErrorAlert :errors="state.errors" />
</div>
</template>
src/file-upload/_shared/ErrorAlert.vue
<!--
Общий блок "File upload error(s)" (`registry-reui/bases/radix/hooks/
use-file-upload.ts` пробрасывает `errors: string[]`), повторяется во всех
паттернах категории с одинаковой разметкой. Деталь реализации — не
экспортируется из index.ts (тот же приём, что `calendar/_shared/
CCalendar.vue`). В проверяемом (пустом/дефолтном) состоянии `errors`
всегда пуст, поэтому этот компонент никогда не монтируется на диф.
-->
<script setup lang="ts">
import { Alert, AlertDescription, AlertTitle } from "@/components/reui/alert"
defineProps<{ errors: string[] }>()
</script>
<template>
<Alert v-if="errors.length > 0" variant="destructive" class="mt-5">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
><circle cx="12" cy="12" r="10" /><line x1="12" x2="12" y1="8" y2="12" /><line x1="12" x2="12.01" y1="16" y2="16" /></svg>
<AlertTitle>File upload error(s)</AlertTitle>
<AlertDescription>
<p v-for="(error, index) in errors" :key="index" class="last:mb-0">{{ error }}</p>
</AlertDescription>
</Alert>
</template>
Установка
npx shadcn-vue@latest add https://revueui.rootapi.dev/r/c-file-upload-2.jsonЗависимости реестра
Источник: порт из ReUI (Keenthemes, MIT)