ui框架-文件上传组件
ui框架-文件上传组件
介绍
UI框架的文件上传组件,基于 Vue 3 开发,提供灵活的文件上传功能,支持单文件/多文件上传、上传进度展示、文件大小限制等特性。组件使用简单,高度可定制,并提供丰富的事件回调。
功能特点
- 支持单文件/多文件上传
- 实时显示上传进度条
- 文件大小自动转换(B/KB/MB)
- 支持文件大小限制
- 支持文件类型限制
- 可自定义按钮文本和样式
- 上传状态通知提醒
- 丰富的事件回调
技术栈
- Vue 3
- Express.js
- Axios
- bbyh-ui-notifaction
安装
npm install bbyh-ui-upload
使用示例
<template><Upload:multiple="true"accept=".jpg,.png,.pdf"upload-url="http://your-api.com/upload"button-text="选择文件":max-size="5 * 1024 * 1024"@upload-success="handleUploadSuccess"@upload-error="handleUploadError"/>
</template><script setup>
const handleUploadSuccess = ({ file, response }) => {console.log(`文件 ${file.name} 上传成功:`, response)
}const handleUploadError = ({ file, error }) => {console.error(`文件 ${file.name} 上传失败:`, error)
}
</script>
API
Props
属性名 | 类型 | 默认值 | 说明 |
---|---|---|---|
multiple | Boolean | true | 是否支持多文件上传 |
accept | String | ‘’ | 接受的文件类型 |
uploadUrl | String | ‘http://localhost:3000/upload’ | 上传地址 |
buttonText | String | ‘选择文件’ | 选择文件按钮文本 |
uploadButtonText | String | ‘开始上传’ | 开始上传按钮文本 |
showUploadButton | Boolean | true | 是否显示上传按钮 |
customStyle | Object | {} | 自定义容器样式 |
buttonStyle | Object | {} | 选择按钮样式 |
uploadButtonStyle | Object | {} | 上传按钮样式 |
progressColor | String | ‘#409eff’ | 进度条颜色 |
maxSize | Number | 0 | 最大文件大小(字节),0表示不限制 |
事件
事件名 | 说明 | 回调参数 |
---|---|---|
file-select | 文件选择后触发 | selectedFiles: Array |
upload-start | 开始上传时触发 | selectedFiles: Array |
upload-progress | 上传进度变化时触发 | { file: Object, progress: Number } |
upload-success | 上传成功时触发 | { file: Object, response: Object } |
upload-error | 上传失败时触发 | { file: Object, error: Error } |
样式定制
组件提供了多个样式定制属性,可以通过 customStyle
、buttonStyle
和 uploadButtonStyle
来自定义容器、选择按钮和上传按钮的样式。
<template><Upload:customStyle="{ border: '2px solid #409eff' }":buttonStyle="{ backgroundColor: '#67c23a' }":uploadButtonStyle="{ backgroundColor: '#409eff' }"progressColor="#67c23a"/>
</template>
服务端示例
组件需要配合服务端接口使用,以下是一个基于 Express.js 的简单示例:
const express = require('express');
const multer = require('multer');
const cors = require('cors');const app = express();
app.use(cors());const storage = multer.diskStorage({destination: 'uploads/',filename: (req, file, cb) => {cb(null, Date.now() + '-' + file.originalname);}
});const upload = multer({ storage });app.post('/upload', upload.single('file'), (req, res) => {if (!req.file) {return res.status(400).json({ message: '没有文件上传' });}res.json({message: '文件上传成功',file: {filename: req.file.filename,size: req.file.size}});
});app.listen(3000, () => {console.log('服务器运行在 http://localhost:3000');
});
源码下载
ui框架-文件上传组件
npm仓库
ui框架-文件上传组件
核心代码
code/src/components/Upload.vue
<template><div class="upload-container"><div class="upload-box" :style="customStyle"><inputtype="file"ref="fileInput"@change="handleFileChange":multiple="multiple":accept="accept"class="file-input"/><div class="upload-btn" :style="buttonStyle" @click="triggerFileInput">{{ buttonText }}</div></div><div class="file-list" v-if="selectedFiles.length"><div class="file-item" v-for="(file, index) in selectedFiles" :key="index"><div class="file-info"><span class="file-name">{{ file.name }}</span><span class="file-size">{{ formatFileSize(file.size) }}</span></div><div class="progress-bar"><div class="progress" :style="{ width: file.progress + '%', backgroundColor: progressColor }"></div></div></div><button class="upload-start" v-if="showUploadButton" :style="uploadButtonStyle" @click="startUpload">{{ uploadButtonText }}</button></div></div>
</template><script setup>
import {ref, defineProps, defineEmits} from 'vue'
import axios from 'axios'
import {showNotification} from 'bbyh-ui-notifaction/components/NotificationManager.js'const props = defineProps({// 是否支持多文件上传multiple: {type: Boolean,default: true},// 接受的文件类型accept: {type: String,default: ''},// 上传地址uploadUrl: {type: String,default: 'http://localhost:3000/upload'},// 选择文件按钮文本buttonText: {type: String,default: '选择文件'},// 开始上传按钮文本uploadButtonText: {type: String,default: '开始上传'},// 是否显示上传按钮showUploadButton: {type: Boolean,default: true},// 自定义样式customStyle: {type: Object,default: () => ({})},// 选择按钮样式buttonStyle: {type: Object,default: () => ({})},// 上传按钮样式uploadButtonStyle: {type: Object,default: () => ({})},// 进度条颜色progressColor: {type: String,default: '#409eff'},// 最大文件大小(字节)maxSize: {type: Number,default: 0}
})const emit = defineEmits(['file-select', // 文件选择事件'upload-start', // 开始上传事件'upload-progress', // 上传进度事件'upload-success', // 上传成功事件'upload-error' // 上传失败事件
])const fileInput = ref()
const selectedFiles = ref([])const triggerFileInput = () => {fileInput.value.click()
}const handleFileChange = (event) => {const files = Array.from(event.target.files)// 检查文件大小if (props.maxSize > 0) {files.forEach(file => {if (file.size > props.maxSize) {showNotification({type: 'error',title: '文件过大',message: `文件 ${file.name} 超过最大限制 ${formatFileSize(props.maxSize)}`,duration: 3000})}})}selectedFiles.value = files.map(file => ({file,name: file.name,size: file.size,progress: 0}))emit('file-select', selectedFiles.value)
}const formatFileSize = (size) => {if (size < 1024) return size + 'B'if (size < 1024 * 1024) return (size / 1024).toFixed(2) + 'KB'return (size / (1024 * 1024)).toFixed(2) + 'MB'
}const startUpload = async () => {emit('upload-start', selectedFiles.value)for (let i = 0; i < selectedFiles.value.length; i++) {const fileInfo = selectedFiles.value[i]fileInfo.progress = 0}for (let i = 0; i < selectedFiles.value.length; i++) {const fileInfo = selectedFiles.value[i]const formData = new FormData()formData.append('file', fileInfo.file)const notification = showNotification({type: 'info',title: '文件上传中',message: `正在上传: ${fileInfo.name}`,progress: true,duration: 0,controlProgress: true})try {const response = await axios.post(props.uploadUrl, formData, {onUploadProgress: (progressEvent) => {const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total)fileInfo.progress = percentCompletednotification.setProgress(percentCompleted)emit('upload-progress', {file: fileInfo,progress: percentCompleted})}})if (response.status === 200) {notification.close()showNotification({type: 'success',title: '上传成功',message: `文件 ${fileInfo.name} 上传完成!`,duration: 3000})emit('upload-success', {file: fileInfo,response: response.data})} else {throw new Error('上传失败')}} catch (error) {notification.close()showNotification({type: 'error',title: '上传错误',message: `文件 ${fileInfo.name} 上传出错:${error.message}`,duration: 3000})emit('upload-error', {file: fileInfo,error})}}
}
</script><style scoped>
.upload-container {padding: 20px;
}.upload-box {border: 2px dashed #dcdfe6;border-radius: 6px;padding: 20px;text-align: center;cursor: pointer;
}.file-input {display: none;
}.upload-btn {display: inline-block;padding: 8px 20px;background-color: #409eff;color: white;border-radius: 4px;cursor: pointer;
}.file-list {margin-top: 20px;
}.file-item {margin-bottom: 10px;padding: 10px;border: 1px solid #ebeef5;border-radius: 4px;
}.file-info {display: flex;justify-content: space-between;margin-bottom: 8px;
}.file-name {font-size: 14px;color: #606266;
}.file-size {font-size: 12px;color: #909399;
}.progress-bar {height: 6px;background-color: #ebeef5;border-radius: 3px;overflow: hidden;
}.progress {height: 100%;transition: width 0.3s ease;
}.upload-start {margin-top: 15px;padding: 8px 20px;background-color: #67c23a;color: white;border: none;border-radius: 4px;cursor: pointer;
}.upload-start:hover {background-color: #85ce61;
}
</style>
效果展示
示例页面
选择文件
文件上传