Upload Component Design Documentation
Overview
The Upload component is a core component in this library used for implementing file upload functionality. It adheres to the library's consistent design philosophy of "Single Responsibility" and "State-Driven" principles, providing a complete and user-friendly file upload solution through upload.vue.
The Upload component focuses on handling file selection, upload process management, and status feedback. It offers users an intuitive and reliable file upload experience, supporting both standard upload and avatar upload modes, multiple file selection, progress feedback, and rich event callbacks, effectively enhancing the user experience during file upload operations.
Design Philosophy
Single Responsibility Principle
The Upload component strictly follows the Single Responsibility Principle:
- File Selection - Handles user file selection operations.
- Upload Management - Manages the entire lifecycle of file uploads.
- Status Feedback - Provides status feedback during the upload process.
- UI Presentation - Provides corresponding interface displays based on different modes.
This design ensures each part of the component has a single responsibility, facilitating maintenance and extension.
State-Driven Design
The Upload component adopts a state-driven design philosophy:
- File State - Manages file upload status via
fileList. - Progress Feedback - Reflects upload progress in real-time via the
progressproperty. - Event-Driven - Implements external notification of state changes through an event mechanism.
This design ensures the predictability and consistency of the component's state.
Core Feature Design
File Management Mechanism
The core of the Upload component lies in its file management mechanism:
const fileList = ref<{ name: string; progress?: number }[]>([])
const previewUrl = ref<string>('')File Management Features:
- State Tracking - Maintains independent upload state for each file.
- Progress Feedback - Tracks and updates upload progress in real-time.
- Preview Support - Provides image preview functionality for avatar mode.
- Multiple File Handling - Supports batch uploading of multiple files.
Upload Process Design
The upload process of the Upload component is designed as follows:
const uploadFile = (file: File) => {
const fd = new FormData()
fd.append('file', file)
const xhr = new XMLHttpRequest()
xhr.open('POST', props.action, true)
// Set request headers
if (props.headers) {
Object.entries(props.headers).forEach(([k, v]) => xhr.setRequestHeader(k, v))
}
// Progress handling
xhr.upload.onprogress = (ev) => {
if (!ev.lengthComputable) return
const percent = Math.round((ev.loaded / ev.total) * 100)
const target = fileList.value.find((f) => f.name === file.name)
if (target) target.progress = percent
safeCall(props.onProgress, percent, file)
emit('progress', percent, file)
}
// Success handling
xhr.onload = () => {
let parsed: any = null
try { parsed = JSON.parse(xhr.responseText || '{}') } catch (e) { parsed = xhr.responseText }
if (xhr.status >= 200 && xhr.status < 300) {
safeCall(props.onSuccess, parsed, file)
emit('success', parsed, file)
} else {
safeCall(props.onError, parsed, file)
emit('error', parsed, file)
}
}
// Error handling
xhr.onerror = () => {
safeCall(props.onError, xhr.responseText, file)
emit('error', xhr.responseText, file)
}
xhr.send(fd)
}Upload Process Features:
- Native XHR - Uses native
XMLHttpRequestfor uploading, avoiding third-party library dependencies. - Progress Tracking - Tracks upload progress in real-time via the
onprogressevent. - Status Feedback - Provides complete success, error, and progress callbacks.
- Error Handling - Comprehensive error handling ensures upload process reliability.
Component Structure Design
Overall Structure
The overall structure of the Upload component is as follows:
<div class="my-upload" :class="{ 'is-avatar': avatar }">
<input ref="fileInput" type="file" :accept="accept" :multiple="multiple" @change="handleChange" style="display: none" />
<div v-if="avatar" class="avatar-wrapper" @click="triggerUpload">
<img v-if="previewUrl || src" :src="previewUrl || src" class="avatar" />
<div v-else class="avatar-placeholder">
<slot name="icon"></slot>
<slot>Click to upload</slot>
</div>
</div>
<button v-else class="upload-btn" @click="triggerUpload">
<slot name="icon"></slot>
<slot>Click to upload</slot>
</button>
<ul v-if="!avatar && showFileList && fileList.length">
<li v-for="(f, i) in fileList" :key="i">
{{ f.name }} <span v-if="f.progress !== undefined">- {{ f.progress }}%</span>
</li>
</ul>
</div>Component Hierarchy
The Upload component consists of several key parts:
- Container -
.my-upload- Wraps the entire upload component. - File Input -
input[type="file"]- Hidden file selection input. - Trigger Area -
.avatar-wrapperor.upload-btn- Area users click to trigger file selection. - Preview Area -
.avatar- Image preview area in avatar mode. - File List -
ul- File list display in standard mode.
Mode Design:
- Standard Upload Mode - Provides button trigger and file list display.
- Avatar Upload Mode - Provides a circular preview area and a clean upload interface.
Interaction Design
File Selection Mechanism
The file selection mechanism of the Upload component is designed as follows:
const triggerUpload = () => fileInput.value?.click()
const handleChange = (e: Event) => {
const files = (e.target as HTMLInputElement).files
if (!files || files.length === 0) return
// Only take the first file for avatar mode; standard mode supports multiple
const toUpload = props.avatar ? files[0] : null
if (props.avatar) {
const f = toUpload as File
previewUrl.value = URL.createObjectURL(f)
fileList.value = [{ name: f.name, progress: 0 }]
uploadFile(f)
} else {
const arr = Array.from(files)
arr.forEach((file) => {
fileList.value.push({ name: file.name, progress: 0 })
uploadFile(file)
})
}
; (e.target as HTMLInputElement).value = ''
}Selection Rules:
- Avatar Mode - Only processes the first selected file.
- Standard Mode - Supports processing multiple selected files.
- Input Reset - Resets the file input after each selection, ensuring the same file can be selected repeatedly.
- Preview Handling - Provides instant preview for avatar mode.
Event Mechanism
The Upload component provides a comprehensive event mechanism:
const emit = defineEmits<{
(e: 'success', res: any, file: File): void
(e: 'error', err: any, file: File): void
(e: 'progress', percent: number, file: File): void
}>()
// ...
safeCall(props.onProgress, percent, file)
emit('progress', percent, file)
// ...
safeCall(props.onSuccess, parsed, file)
emit('success', parsed, file)
// ...
safeCall(props.onError, parsed, file)
emit('error', parsed, file)Event Features:
- Dual Callbacks - Supports both prop callbacks and emit events.
- Safe Invocation - Ensures safe invocation of callback functions via
safeCall. - Rich Parameters - Provides detailed upload results and file information.
- Progress Feedback - Provides real-time upload progress information.
Extensibility Design
The Upload component offers excellent extensibility:
- Mode Extension - Supports standard upload and avatar upload modes via the
avatarproperty. - File Types - Supports custom accepted file types via the
acceptproperty. - Event Extension - Supports custom event handling via
onSuccess,onError, andonProgressproperties. - UI Extension - Supports custom icons and text via slot mechanisms.
- Request Customization - Supports custom request headers via the
headersproperty.
Style Details
The Upload component features the following style handling characteristics:
- Mode Differentiation - Distinguishes different modes via the
is-avatarclass.scss.my-upload { display: inline-block; } .avatar-wrapper { width: 100px; height: 100px; border-radius: 50%; overflow: hidden; // ... } - Status Feedback - Provides clear upload status through progress display.
- Visual Hierarchy - Creates a clear visual hierarchy through color and spacing.
- Responsive Design - Adapts to different screen sizes and usage scenarios.
- Clean UI - Keeps the interface simple, highlighting core functionality.
Performance Optimization
Performance is optimized through:
- Memory Management - Uses
URL.createObjectURLto create preview URLs, avoiding storing large files directly. - Input Reset - Resets the file input after each selection, ensuring the same file can be selected repeatedly.
- Progress Calculation - Updates progress information only when necessary.
- Error Handling - Comprehensive error handling ensures a single file upload failure does not affect the overall process.
- Resource Release - Releases resources occupied by preview URLs when appropriate.
Design Value
The Upload component embodies the design philosophy of our component library:
- Separation of Concerns - Separates file selection, upload management, and status feedback.
- State-Driven - Drives interface updates through state changes.
- Modal Design - Supports two common scenarios: standard upload and avatar upload.
- Interaction Consistency - Maintains consistent interaction patterns with other components in the library.
- Customizability - Provides rich customization capabilities through properties and slots.