mirror of
https://gitee.com/ccnetcore/Yi
synced 2026-03-20 08:26:37 +08:00
247 lines
7.1 KiB
Vue
247 lines
7.1 KiB
Vue
<!-- 文件上传 -->
|
|
<script setup lang="ts">
|
|
import type { FileItem } from '@/stores/modules/files';
|
|
import { useFileDialog } from '@vueuse/core';
|
|
import { ElMessage } from 'element-plus';
|
|
import Popover from '@/components/Popover/index.vue';
|
|
import SvgIcon from '@/components/SvgIcon/index.vue';
|
|
import { useFilesStore } from '@/stores/modules/files';
|
|
|
|
const filesStore = useFilesStore();
|
|
|
|
// 文件大小限制 3MB
|
|
const MAX_FILE_SIZE = 3 * 1024 * 1024;
|
|
|
|
/* 弹出面板 开始 */
|
|
const popoverStyle = ref({
|
|
padding: '4px',
|
|
height: 'fit-content',
|
|
background: 'var(--el-bg-color, #fff)',
|
|
border: '1px solid var(--el-border-color-light)',
|
|
borderRadius: '8px',
|
|
boxShadow: '0 2px 12px 0 rgba(0, 0, 0, 0.1)',
|
|
});
|
|
const popoverRef = ref();
|
|
/* 弹出面板 结束 */
|
|
|
|
const { reset, open, onChange } = useFileDialog({
|
|
// 允许图片和文件
|
|
accept: 'image/*,application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
directory: false, // 是否允许选择文件夹
|
|
multiple: true, // 是否允许多选
|
|
});
|
|
|
|
// 压缩图片
|
|
function compressImage(file: File, maxWidth = 1024, maxHeight = 1024, quality = 0.8): Promise<Blob> {
|
|
return new Promise((resolve, reject) => {
|
|
const reader = new FileReader();
|
|
reader.onload = (e) => {
|
|
const img = new Image();
|
|
img.onload = () => {
|
|
const canvas = document.createElement('canvas');
|
|
let width = img.width;
|
|
let height = img.height;
|
|
|
|
// 计算缩放比例
|
|
if (width > maxWidth || height > maxHeight) {
|
|
const ratio = Math.min(maxWidth / width, maxHeight / height);
|
|
width = width * ratio;
|
|
height = height * ratio;
|
|
}
|
|
|
|
canvas.width = width;
|
|
canvas.height = height;
|
|
|
|
const ctx = canvas.getContext('2d')!;
|
|
ctx.drawImage(img, 0, 0, width, height);
|
|
|
|
// 转换为 Blob
|
|
canvas.toBlob(
|
|
(blob) => {
|
|
if (blob) {
|
|
resolve(blob);
|
|
} else {
|
|
reject(new Error('压缩失败'));
|
|
}
|
|
},
|
|
file.type,
|
|
quality
|
|
);
|
|
};
|
|
img.onerror = reject;
|
|
img.src = e.target?.result as string;
|
|
};
|
|
reader.onerror = reject;
|
|
reader.readAsDataURL(file);
|
|
});
|
|
}
|
|
|
|
// 将 Blob 转换为 base64
|
|
function blobToBase64(blob: Blob): Promise<string> {
|
|
return new Promise((resolve, reject) => {
|
|
const reader = new FileReader();
|
|
reader.onload = () => {
|
|
resolve(reader.result as string);
|
|
};
|
|
reader.onerror = reject;
|
|
reader.readAsDataURL(blob);
|
|
});
|
|
}
|
|
|
|
onChange(async (files) => {
|
|
if (!files)
|
|
return;
|
|
|
|
const arr = [] as FileItem[];
|
|
|
|
for (let i = 0; i < files!.length; i++) {
|
|
const file = files![i];
|
|
|
|
// 验证文件大小
|
|
if (file.size > MAX_FILE_SIZE) {
|
|
ElMessage.error(`文件 ${file.name} 超过 3MB 限制,已跳过`);
|
|
continue;
|
|
}
|
|
|
|
const isImage = file.type.startsWith('image/');
|
|
|
|
// 压缩并转换为base64
|
|
let base64 = '';
|
|
let previewUrl = '';
|
|
if (isImage) {
|
|
try {
|
|
// 先压缩图片
|
|
const compressedBlob = await compressImage(file, 1024, 1024, 0.8);
|
|
// 再转换为 base64
|
|
base64 = await blobToBase64(compressedBlob);
|
|
// 使用压缩后的图片作为预览
|
|
previewUrl = base64;
|
|
|
|
// 计算压缩比例
|
|
const originalSize = (file.size / 1024).toFixed(2);
|
|
const compressedSize = (compressedBlob.size / 1024).toFixed(2);
|
|
console.log(`图片压缩: ${file.name} - 原始: ${originalSize}KB, 压缩后: ${compressedSize}KB`);
|
|
} catch (error) {
|
|
console.error('压缩图片失败:', error);
|
|
ElMessage.error(`${file.name} 压缩失败`);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
arr.push({
|
|
uid: crypto.randomUUID(), // 不写 uid,文件列表展示不出来,elx 1.2.0 bug 待修复
|
|
name: file.name,
|
|
fileSize: file.size,
|
|
file,
|
|
maxWidth: '200px',
|
|
showDelIcon: true, // 显示删除图标
|
|
imgPreview: isImage, // 图片才显示预览
|
|
imgVariant: 'square', // 图片预览的形状
|
|
url: isImage ? previewUrl : undefined, // 使用压缩后的 base64 作为预览地址
|
|
isUploaded: true, // 直接标记为已完成
|
|
base64: isImage ? base64 : undefined, // 保存压缩后的base64
|
|
});
|
|
}
|
|
|
|
if (arr.length > 0) {
|
|
filesStore.setFilesList([...filesStore.filesList, ...arr]);
|
|
ElMessage.success(`已添加 ${arr.length} 个文件`);
|
|
}
|
|
|
|
// 重置文件选择器
|
|
nextTick(() => reset());
|
|
});
|
|
|
|
function handleUploadFiles() {
|
|
open();
|
|
popoverRef.value.hide();
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="files-select">
|
|
<Popover
|
|
ref="popoverRef"
|
|
placement="top-start"
|
|
:offset="[4, 0]"
|
|
popover-class="popover-content"
|
|
:popover-style="popoverStyle"
|
|
trigger="clickTarget"
|
|
>
|
|
<template #trigger>
|
|
<div
|
|
class="flex items-center gap-4px p-10px rounded-10px cursor-pointer font-size-14px border-1px border-[rgba(0,0,0,0.08)] border-solid hover:bg-[rgba(0,0,0,.04)]"
|
|
>
|
|
<el-icon>
|
|
<Paperclip />
|
|
</el-icon>
|
|
</div>
|
|
</template>
|
|
|
|
<div class="popover-content-box">
|
|
<div
|
|
class="popover-content-item flex items-center gap-4px p-10px rounded-10px cursor-pointer font-size-14px hover:bg-[rgba(0,0,0,.04)]"
|
|
@click="handleUploadFiles"
|
|
>
|
|
<el-icon>
|
|
<Upload />
|
|
</el-icon>
|
|
<div class="font-size-14px">
|
|
上传文件或图片
|
|
</div>
|
|
</div>
|
|
|
|
<Popover
|
|
placement="right-end"
|
|
:offset="[8, 4]"
|
|
popover-class="popover-content"
|
|
:popover-style="popoverStyle"
|
|
trigger="hover"
|
|
:hover-delay="100"
|
|
>
|
|
<template #trigger>
|
|
<div
|
|
class="popover-content-item flex items-center gap-4px p-10px rounded-10px cursor-pointer font-size-14px hover:bg-[rgba(0,0,0,.04)]"
|
|
>
|
|
<SvgIcon name="code" size="16" />
|
|
<div class="font-size-14px">
|
|
上传代码
|
|
</div>
|
|
|
|
<el-icon class="ml-auto">
|
|
<ArrowRight />
|
|
</el-icon>
|
|
</div>
|
|
</template>
|
|
|
|
<div class="popover-content-box">
|
|
<div
|
|
class="popover-content-item flex items-center gap-4px p-10px rounded-10px cursor-pointer font-size-14px hover:bg-[rgba(0,0,0,.04)]"
|
|
@click="
|
|
() => {
|
|
ElMessage.warning('暂未开放');
|
|
}
|
|
"
|
|
>
|
|
代码文件
|
|
</div>
|
|
|
|
<div
|
|
class="popover-content-item flex items-center gap-4px p-10px rounded-10px cursor-pointer font-size-14px hover:bg-[rgba(0,0,0,.04)]"
|
|
@click="
|
|
() => {
|
|
ElMessage.warning('暂未开放');
|
|
}
|
|
"
|
|
>
|
|
代码文件夹
|
|
</div>
|
|
</div>
|
|
</Popover>
|
|
</div>
|
|
</Popover>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped lang="scss"></style>
|