多维表格
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

191 lines
5.9 KiB

<script setup lang="ts">
import { useAttachmentCell } from '../utils'
const emits = defineEmits<{
'update:visible': [value: boolean]
'upload': [fileList: File[]]
}>()
const dropZoneRef = ref<HTMLDivElement>()
const tempFiles = ref<File[]>([])
const { isLoading } = useAttachmentCell()!
const { files, open: _open } = useFileDialog({
reset: true,
})
watch(files, (newFiles) => {
if (!newFiles) return
Object.values(newFiles).forEach((file) => {
tempFiles.value.push(file)
})
})
const onDrop = (files: File[], event: DragEvent) => {
tempFiles.value.push(...files)
event.preventDefault()
event.stopPropagation()
}
const thumbnails = computedAsync(async () => {
const map = new Map()
await Promise.all(
tempFiles.value.map(async (file) => {
const thumbnail = await createThumbnail(file)
if (thumbnail) {
map.set(file, thumbnail)
}
}),
)
return map
})
const onRemoveFileClick = (file: File) => {
tempFiles.value = tempFiles.value.filter((f) => f !== file)
}
const { isOverDropZone } = useDropZone(dropZoneRef, onDrop)
const clearAll = () => {
tempFiles.value = []
}
const open = () => {
_open()
}
const closeMenu = () => {
emits('update:visible', false)
}
onBeforeUnmount(() => {
tempFiles.value = []
})
</script>
<template>
<div
:class="{
'flex flex-col relative justify-center items-center': !tempFiles.length,
}"
class="w-full p-2 h-full"
>
<NcTooltip v-if="tempFiles.length === 0" class="absolute top-3 right-3">
<NcButton type="text" class="!border-0" size="xsmall" @click="closeMenu">
<GeneralIcon icon="close" />
</NcButton>
<template #title> {{ $t('general.close') }} </template>
</NcTooltip>
<div v-if="tempFiles.length > 0" class="flex w-full border-b-1 py-1 h-9.5 items-center justify-between top-0">
<NcButton type="text" size="small" @click="clearAll">
{{ $t('labels.clearAllFiles') }}
</NcButton>
<span class="text-xs">
{{ tempFiles.length }} files, total size:
{{
formatBytes(
tempFiles.reduce((acc, file) => acc + file.size, 0),
0,
)
}}
</span>
<NcButton type="text" size="small" @click="open">
<div class="flex gap-1 items-center">
<component :is="iconMap.plus" />
{{ $t('labels.addMore') }}
</div>
</NcButton>
</div>
<div
ref="dropZoneRef"
:class="{
'border-brand-500': isOverDropZone,
'border-dashed border-2': !tempFiles.length,
}"
data-testid="attachment-drop-zone"
:style="`height: ${tempFiles.length > 0 ? '324px' : '100%'}`"
class="flex flex-col items-center justify-center h-full w-full flex-grow-1 rounded-lg"
@click="tempFiles.length > 0 ? () => {} : open()"
>
<div v-if="!tempFiles.length" class="flex cursor-pointer items-center justify-center flex-col gap-2">
<template v-if="!isOverDropZone">
<component :is="iconMap.upload" class="w-6 h-6" />
<h1>
{{ $t('labels.clickTo') }}
<span class="font-semibold text-brand-500"> {{ $t('labels.browseFiles') }} </span>
{{ $t('general.or') }}
<span class="font-semibold"> {{ $t('labels.dragFilesHere') }} </span>
{{ $t('labels.toUpload') }}
</h1>
</template>
<template v-if="isOverDropZone">
<component :is="iconMap.upload" class="w-5 text-brand-500 h-5" />
<h1 class="text-brand-500 font-bold">{{ $t('labels.dropHere') }}</h1>
</template>
</div>
<template v-else>
<div
class="grid overflow-y-auto flex-grow-1 nc-scrollbar-md grid-cols-4 w-full h-full items-start py-2 justify-center gap-4"
>
<div v-for="file in tempFiles" :key="file.name" class="flex gap-1.5 group min-w-34 max-w-28 pb-4 flex-col relative">
<div
v-if="!thumbnails.get(file)"
style="height: 140px"
class="flex items-center justify-center rounded-md bg-gray-300"
>
<component :is="iconMap.file" class="w-16 h-16" />
</div>
<img v-else :src="thumbnails.get(file)" style="height: 140px" alt="thumbnail" class="rounded-md object-cover" />
<div class="relative text-[12px] font-semibold items-center text-gray-800 flex">
<NcTooltip class="flex-auto truncate" placement="bottom">
<template #title> {{ file.name }} </template>
{{ file.name }}
</NcTooltip>
<div class="flex-none hide-ui transition-all transition-ease-in-out !h-4 flex items-center bg-white">
<NcTooltip placement="bottom">
<template #title> {{ $t('title.removeFile') }} </template>
<component :is="iconMap.delete" class="!text-red-500 w-3 h-3 cursor-pointer" @click="onRemoveFileClick(file)" />
</NcTooltip>
</div>
</div>
<div class="flex-none text-[10px] font-semibold text-gray-500">
{{ formatBytes(file.size, 0) }}
</div>
</div>
</div>
</template>
</div>
<div v-if="tempFiles.length" class="flex gap-2 pt-1 bg-white w-full items-center justify-end">
<NcButton :disabled="isLoading" type="secondary" size="small" @click="closeMenu">
{{ $t('labels.cancel') }}
</NcButton>
<NcButton :loading="isLoading" data-testid="nc-upload-file" size="small" @click="emits('upload', tempFiles)">
<template v-if="isLoading">
{{ $t('labels.uploading') }}
</template>
<template v-else> {{ $t('general.upload') }} {{ tempFiles.length }} {{ $t('objects.files') }} </template>
</NcButton>
</div>
</div>
</template>
<style lang="scss">
.hide-ui {
@apply h-0 w-0 overflow-hidden whitespace-nowrap;
.group:hover & {
@apply h-auto w-auto overflow-visible whitespace-normal;
}
}
</style>