fairy-webuploader
Version:
301 lines (255 loc) • 11.8 kB
JavaScript
import AbstractTemplate from "./AbstractTemplate.js";
export default class ModernFileTemplate extends AbstractTemplate{
constructor(el, options) {
super(el, options);
}
initStyles(styleId) {
this.brandColor = '#4CAF50';
this.isDarkMode = true;
const style = document.createElement('style');
style.id = styleId;
style.textContent = `
/* 基础样式 */
.file-uploader-container {
background: linear-gradient(135deg, #f5f5f5, #f0eaea);
border-radius: 8px;
border: 1px solid rgba(0, 0, 0, 0.1);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
padding: 40px;
text-align: center;
width: 100%;
max-width: 100%;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
/* 几何波纹装饰 */
.file-uploader-container::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="rgba(0,0,0,0.03)" stroke-width="1"><path d="M200 0v400M0 200h400"/></svg>');
background-size: 50px 50px;
opacity: 0.2;
pointer-events: none;
animation: wave 10s linear infinite;
}
wave {
0% { transform: translate(-25px, -25px); }
100% { transform: translate(25px, 25px); }
}
/* 图标 */
.upload-icon {
width: 64px;
height: 64px;
fill: #999;
transition: transform 0.3s ease;
}
.file-uploader-container:hover .upload-icon {
transform: scale(1.1);
}
/* 提示文案 */
.upload-text {
font-family: 'SF Pro', 'Roboto', sans-serif;
font-size: 14px;
color: #999;
margin-top: 16px;
transition: color 0.3s ease;
}
/* 悬停状态 */
.file-uploader-container:hover {
background-color: #fafafa;
border-color: ${this.brandColor};
cursor: pointer;
}
.file-uploader-container:hover .upload-text {
color: ${this.brandColor};
}
/* 拖拽激活状态 */
.file-uploader-container.drag-active {
border: 2px dashed rgba(${parseInt(this.brandColor.slice(1, 3), 16)}, ${parseInt(this.brandColor.slice(3, 5), 16)}, ${parseInt(this.brandColor.slice(5, 7), 16)}, 0.3);
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" fill="none" stroke="rgba(0,0,0,0.05)" stroke-width="1"><path d="M0 20h40M20 0v40"/></svg>');
background-size: 20px 20px;
animation: grid-move 2s linear infinite;
}
grid-move {
0% { background-position: 0 0; }
100% { background-position: 20px 20px; }
}
/* 上传进度条 */
.progress-bar {
height: 4px;
background-color: #eee;
border-radius: 2px;
margin-top: 16px;
overflow: hidden;
}
.progress {
height: 100%;
background-color: ${this.brandColor};
width: 0%;
transition: width 0.3s ease;
position: relative;
}
.progress::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
animation: shine 2s infinite;
}
shine {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
/* 成功提示 */
.success-icon {
width: 32px;
height: 32px;
fill: ${this.brandColor};
opacity: 0;
transition: opacity 0.3s ease, transform 0.3s ease;
transform: scale(0.5);
}
.success-icon.show {
opacity: 1;
transform: scale(1);
}
/* 错误状态 */
.error-state {
background-color: #ffebee;
border-color: #ef5350;
}
.error-text {
color: #ef5350;
display: flex;
align-items: center;
justify-content: center;
}
.error-text::before {
content: '😅';
margin-right: 8px;
}
/* 暗黑模式 */
.dark-mode .file-uploader-container {
background-color: #3a3a3a;
border-color: #555;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.dark-mode .file-uploader-container::before {
opacity: 0.1;
}
.dark-mode .upload-icon {
fill: #bbb;
}
.dark-mode .upload-text {
color: #bbb;
}
.dark-mode .file-uploader-container:hover {
background-color: #444;
border-color: ${this.brandColor};
}
.dark-mode .file-uploader-container:hover .upload-text {
color: ${this.brandColor};
}
.dark-mode .file-uploader-container.drag-active {
border-color: rgba(${parseInt(this.brandColor.slice(1, 3), 16)}, ${parseInt(this.brandColor.slice(3, 5), 16)}, ${parseInt(this.brandColor.slice(5, 7), 16)}, 0.3);
}
.dark-mode .progress-bar {
background-color: #555;
}
.dark-mode .error-state {
background-color: #4a3a3a;
border-color: #ef5350;
}
.dark-mode .error-text {
color: #ef5350;
}
`;
document.head.appendChild(style);
}
createUploader() {
this.uploaderContainer = document.createElement('div');
this.uploaderContainer.className = 'file-uploader-container';
this.uploaderContainer.id = AbstractTemplate.uploaderContainerId
const uploadIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
uploadIcon.classList.add('upload-icon');
uploadIcon.setAttribute('viewBox', '0 0 24 24');
uploadIcon.innerHTML = `
<path d="M19 13h-6v6h-2v-6H5l7-7 7 7zM5 5v2h14V5H5z"/>
`;
const uploadText = document.createElement('div');
uploadText.className = 'upload-text';
uploadText.textContent = '点击或拖拽文件到此处';
const input = document.createElement('input');
input.id = AbstractTemplate.uploaderInputId;
input.type = 'file';
input.style.display = 'none';
input.multiple = this.options?.multiple || true;
input.accept = this.options?.accept || '*';
this.progressBar = document.createElement('div');
this.progressBar.className = 'progress-bar';
this.progress = document.createElement('div');
this.progress.className = 'progress';
this.progressBar.appendChild(this.progress);
this.successIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
this.successIcon.classList.add('success-icon');
this.successIcon.setAttribute('viewBox', '0 0 24 24');
this.successIcon.innerHTML = `
<path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"/>
`;
this.uploaderContainer.appendChild(uploadIcon);
this.uploaderContainer.appendChild(uploadText);
this.uploaderContainer.appendChild(input);
this.uploaderContainer.appendChild(this.progressBar);
this.uploaderContainer.appendChild(this.successIcon);
this.container.appendChild(this.uploaderContainer);
this.input = input;
}
setupEventListeners() {
this.uploaderContainer.addEventListener('click', () => {
this.input.click();
});
this.input.addEventListener('change', (e) => {
this.handleFileSelect(e.target.files);
});
this.uploaderContainer.addEventListener('dragover', (e) => {
e.preventDefault();
this.uploaderContainer.classList.add('drag-active');
});
this.uploaderContainer.addEventListener('dragleave', () => {
this.uploaderContainer.classList.remove('drag-active');
});
this.uploaderContainer.addEventListener('drop', (e) => {
e.preventDefault();
this.uploaderContainer.classList.remove('drag-active');
this.handleFileSelect(e.dataTransfer.files);
});
// 模拟暗黑模式切换
document.addEventListener('keydown', (e) => {
if (e.key === 'd') {
this.toggleDarkMode();
}
});
}
showSuccess() {
this.successIcon.classList.add('show');
// 微震动效果
this.uploaderContainer.style.animation = 'shake 0.5s';
setTimeout(() => {
this.uploaderContainer.style.animation = '';
this.progress.style.width = '0%';
this.successIcon.classList.remove('show');
}, 1500);
}
toggleDarkMode() {
this.isDarkMode = !this.isDarkMode;
document.body.classList.toggle('dark-mode', this.isDarkMode);
}
}