mirror of
https://github.com/keven1024/015.git
synced 2026-05-26 07:08:02 +00:00
feat(front): implement FileUpload and FileUploadField components for file drag-and-drop functionality and preview
This commit is contained in:
68
front/components/Field/FileUploadField.vue
Normal file
68
front/components/Field/FileUploadField.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<script setup lang="ts">
|
||||
import FileUpload from '~/components/FileUpload.vue'
|
||||
import { cx } from 'class-variance-authority'
|
||||
import { filesize } from 'filesize'
|
||||
|
||||
const props = defineProps<{
|
||||
name: string
|
||||
rules?: string
|
||||
}>()
|
||||
const { value, setValue } = useField<File>(props?.name, props?.rules)
|
||||
watch(value, (v) => {
|
||||
console.log('value', v)
|
||||
})
|
||||
|
||||
const imageUrl = computed(() => {
|
||||
if (value?.value?.type?.startsWith('image/')) {
|
||||
return URL.createObjectURL(value?.value)
|
||||
}
|
||||
return null
|
||||
})
|
||||
|
||||
const fileInfo = computed(() => {
|
||||
const [,name, ext] = value?.value?.name?.match(/^(.+)\.(.+)$/) || []
|
||||
return { name, ext }
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (imageUrl.value) {
|
||||
URL.revokeObjectURL(imageUrl.value)
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FileUpload @onChange="(file) => {
|
||||
setValue(file)
|
||||
}" v-slot="{ isOverDropZone }">
|
||||
<div :class="cx('bg-white/50 rounded-md p-2 w-full h-40 flex flex-col items-center justify-center border border-dashed border-black/20 cursor-pointer text-gray-500 gap-3',
|
||||
isOverDropZone && '!bg-green-100/50 '
|
||||
)">
|
||||
<template v-if="!!value">
|
||||
<div v-if="!!imageUrl" class="flex size-16">
|
||||
<div class="object-contain mx-auto">
|
||||
<img :src="imageUrl" class="w-full h-full border border-black/20 rounded" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!imageUrl" class="size-16 flex justify-center items-center rounded-xl bg-white/80">
|
||||
<LucideFile class="size-10" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-0.5 items-center">
|
||||
<div class="flex max-w-30 w-full">
|
||||
<div class="truncate">{{ fileInfo?.name }}</div>
|
||||
<div>{{ `.${fileInfo?.ext}` }}</div>
|
||||
</div>
|
||||
<div class="text-xs opacity-50">{{ filesize(value?.size) }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<LucideUpload class="size-10" />
|
||||
<div class="text-sm">
|
||||
拖拽文件 或 点击上传
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</div>
|
||||
</FileUpload>
|
||||
</template>
|
||||
59
front/components/FileUpload.vue
Normal file
59
front/components/FileUpload.vue
Normal file
@@ -0,0 +1,59 @@
|
||||
<script setup lang="ts">
|
||||
import { useDropZone } from '@vueuse/core'
|
||||
const dropZoneRef = ref()
|
||||
|
||||
const props = defineProps<{
|
||||
accept?: string[]
|
||||
}>()
|
||||
|
||||
const accept = computed(() => (props?.accept || ['image/*', 'video/*'])?.join(','))
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'onChange', file: File): void
|
||||
}>()
|
||||
|
||||
const { isOverDropZone } = useDropZone(dropZoneRef, {
|
||||
onDrop: (file) => {
|
||||
if (file?.[0]) {
|
||||
emit('onChange', file?.[0])
|
||||
}
|
||||
},
|
||||
// 指定要接收的数据类型
|
||||
dataTypes: (types) => {
|
||||
for (const type of types) {
|
||||
for (const acceptType of accept.value.split(',')) {
|
||||
if (acceptType?.endsWith('*')) {
|
||||
const [acceptTypePrefix,] = acceptType?.split('/')
|
||||
if (type?.startsWith(acceptTypePrefix)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if (acceptType === type) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
},
|
||||
// 控制多文件拖放
|
||||
multiple: false,
|
||||
// 是否阻止未处理事件的默认行为
|
||||
preventDefaultForUnhandled: false,
|
||||
})
|
||||
|
||||
const { open, onChange } = useFileDialog({
|
||||
accept: accept.value, // Set to accept only image files
|
||||
directory: false,
|
||||
})
|
||||
onChange((files) => {
|
||||
if (files?.[0]) {
|
||||
emit('onChange', files?.[0])
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="dropZoneRef" @click="open">
|
||||
<slot :isOverDropZone="isOverDropZone" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -29,6 +29,7 @@
|
||||
"@vee-validate/rules": "^4.15.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"filesize": "^10.1.6",
|
||||
"lodash-es": "^4.17.21",
|
||||
"lucide-vue-next": "^0.487.0",
|
||||
"motion-v": "1.0.0-beta.2",
|
||||
|
||||
Reference in New Issue
Block a user