<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>U盘模拟器(多主题+自由容量)</title>
<style>
:root {
--bg-color: #ffffff;
--text-color: #333;
--card-bg: #f9f9f9;
--button-bg: #4caf50;
--button-text: #fff;
--border-color: #ddd;
}
[data-theme="dark"] {
--bg-color: #121212;
--text-color: #e0e0e0;
--card-bg: #1f1f1f;
--button-bg: #2196f3;
--button-text: #fff;
--border-color: #444;
}
[data-theme="blue"] {
--bg-color: #e3f2fd;
--text-color: #0d47a1;
--card-bg: #bbdefb;
--button-bg: #0d47a1;
--button-text: #fff;
--border-color: #90caf9;
}
[data-theme="purple"] {
--bg-color: #f3e5f5;
--text-color: #4a148c;
--card-bg: #e1bee7;
--button-bg: #9c27b0;
--button-text: #fff;
--border-color: #ce93d8;
}
[data-theme="green"] {
--bg-color: #e8f5e9;
--text-color: #1b5e20;
--card-bg: #c8e6c9;
--button-bg: #2e7d32;
--button-text: #fff;
--border-color: #a5d6a7;
}
[data-theme="orange"] {
--bg-color: #fff3e0;
--text-color: #e65100;
--card-bg: #ffe0b2;
--button-bg: #ef6c00;
--button-text: #fff;
--border-color: #ffb74d;
}
[data-theme="pink"] {
--bg-color: #fce4ec;
--text-color: #880e4f;
--card-bg: #f8bbd0;
--button-bg: #c2185b;
--button-text: #fff;
--border-color: #f48fb1;
}
[data-theme="gray"] {
--bg-color: #f5f5f5;
--text-color: #212121;
--card-bg: #e0e0e0;
--button-bg: #607d8b;
--button-text: #fff;
--border-color: #9e9e9e;
}
body {
background: var(--bg-color);
color: var(--text-color);
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 960px;
margin: 30px auto;
padding: 20px;
border: 1px solid var(--border-color);
border-radius: 12px;
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
transition: all 0.3s ease;
}
h1 {
text-align: center;
margin-bottom: 20px;
}
.control-section {
display: flex;
flex-wrap: wrap;
gap: 10px;
justify-content: center;
margin-bottom: 15px;
}
#currentPath {
text-align: center;
margin: 10px 0;
font-weight: bold;
}
#file-list {
margin-top: 20px;
}
.file-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
border: 1px solid var(--border-color);
background: var(--card-bg);
border-radius: 8px;
margin-bottom: 10px;
transition: transform 0.2s;
}
.file-item:hover {
transform: scale(1.02);
}
.actions {
display: flex;
gap: 5px;
flex-wrap: wrap;
}
.capacity {
text-align: center;
margin-top: 15px;
font-weight: bold;
}
.progress-bar {
width: 100%;
background: #eee;
border-radius: 5px;
overflow: hidden;
margin-top: 10px;
}
.progress {
height: 24px;
background: var(--button-bg);
text-align: center;
color: var(--button-text);
display: flex;
align-items: center;
justify-content: center;
font-size: 0.9em;
transition: width 0.3s ease;
}
button, select, input[type="number"], input[type="text"] {
cursor: pointer;
padding: 6px 12px;
border: none;
border-radius: 6px;
background: var(--button-bg);
color: var(--button-text);
transition: all 0.3s ease;
}
button:hover, select:hover {
opacity: 0.9;
transform: translateY(-2px);
}
input[type="number"], input[type="text"] {
color: #000;
background: #fff;
}
</style>
</head>
<body>
<h1>U盘模拟器(可自定义容量与单位)</h1>
<div class="control-section">
<input type="file" id="fileInput" multiple>
<input type="text" id="folderNameInput" placeholder="新建文件夹名称">
<button id="createFolderBtn">新建文件夹</button>
</div>
<div class="control-section">
<input type="number" id="capacityInput" placeholder="容量数值">
<select id="capacityUnit">
<option value="KB">KB</option>
<option value="MB" selected>MB</option>
<option value="GB">GB</option>
<option value="TB">TB</option>
</select>
<button id="setCapacityBtn">设置容量</button>
<button id="backBtn">返回上一级</button>
<button id="formatBtn">格式化U盘</button>
<select id="themeSelector">
<option value="light">白天模式</option>
<option value="dark">夜晚模式</option>
<option value="blue">蓝色主题</option>
<option value="purple">紫色主题</option>
<option value="green">绿色主题</option>
<option value="orange">橙色主题</option>
<option value="pink">粉色主题</option>
<option value="gray">灰色主题</option>
</select>
</div>
<div id="currentPath">当前位置: 根目录</div>
<div id="file-list"></div>
<div class="capacity" id="capacity">已用空间: 0 MB / 500 MB</div>
<div class="progress-bar">
<div class="progress" id="progress">0%</div>
</div>
<script>
let maxCapacity = 500;
let usedCapacity = 0;
const fileSystem = { '/': [] };
let currentPath = '/';
const pathStack = ['/'];
const fileList = document.getElementById('file-list');
const capacityDisplay = document.getElementById('capacity');
const progressBar = document.getElementById('progress');
const currentPathDisplay = document.getElementById('currentPath');
document.addEventListener('DOMContentLoaded', () => {
const savedTheme = localStorage.getItem('usbTheme') || 'light';
document.body.setAttribute('data-theme', savedTheme);
document.getElementById('themeSelector').value = savedTheme;
});
document.getElementById('themeSelector').addEventListener('change', e => {
document.body.setAttribute('data-theme', e.target.value);
localStorage.setItem('usbTheme', e.target.value);
});
document.getElementById('fileInput').addEventListener('change', e => {
Array.from(e.target.files).forEach(file => addFile(file));
e.target.value = '';
});
document.getElementById('createFolderBtn').addEventListener('click', () => {
const folderName = document.getElementById('folderNameInput').value.trim();
if (!folderName) return alert('请输入文件夹名称');
const folderPath = `${currentPath}${folderName}/`;
if (fileSystem[folderPath]) return alert('该文件夹已存在');
fileSystem[folderPath] = [];
fileSystem[currentPath].push({ type: 'folder', name: folderName, path: folderPath });
renderFileList();
document.getElementById('folderNameInput').value = '';
});
document.getElementById('setCapacityBtn').addEventListener('click', () => {
const value = parseFloat(document.getElementById('capacityInput').value);
const unit = document.getElementById('capacityUnit').value;
if (isNaN(value) || value <= 0) {
alert('请输入有效的容量数值');
return;
}
const unitMap = {
KB: 1 / 1024,
MB: 1,
GB: 1024,
TB: 1024 * 1024
};
maxCapacity = value * unitMap[unit];
updateCapacity();
alert(`U盘容量已设置为 ${value} ${unit}(约 ${maxCapacity.toFixed(2)} MB)`);
});
document.getElementById('backBtn').addEventListener('click', () => {
if (pathStack.length > 1) {
pathStack.pop();
currentPath = pathStack[pathStack.length - 1];
updatePathDisplay();
renderFileList();
} else {
alert('已在根目录');
}
});
document.getElementById('formatBtn').addEventListener('click', () => {
if (confirm('确定要格式化U盘吗?所有文件将被删除!')) {
Object.keys(fileSystem).forEach(key => delete fileSystem[key]);
fileSystem['/'] = [];
usedCapacity = 0;
currentPath = '/';
pathStack.length = 1;
updateCapacity();
renderFileList();
updatePathDisplay();
}
});
function addFile(file) {
const fileSizeMB = (file.size / (1024 * 1024)).toFixed(2);
if (usedCapacity + parseFloat(fileSizeMB) > maxCapacity) {
alert(`无法添加文件 ${file.name} ,容量不足。`);
return;
}
usedCapacity += parseFloat(fileSizeMB);
fileSystem[currentPath].push({ type: 'file', file, sizeMB: fileSizeMB });
updateCapacity();
renderFileList();
}
function renderFileList() {
fileList.innerHTML = '';
fileSystem[currentPath].forEach(item => {
const div = document.createElement('div');
div.className = 'file-item';
if (item.type === 'folder') {
div.innerHTML = `<span>📁 ${item.name}</span>
<div class="actions">
<button onclick="enterFolder('${item.path}')">进入</button>
<button onclick="removeFolder('${item.path}')">删除</button>
</div>`;
} else {
div.innerHTML = `<span>📄 ${item.file.name} (${item.sizeMB} MB)</span>
<div class="actions">
<button onclick="previewFile('${item.file.name}')">预览</button>
<button onclick="downloadFile('${item.file.name}')">下载</button>
<button onclick="renameFile('${item.file.name}')">重命名</button>
<button onclick="removeFile('${item.file.name}')">删除</button>
</div>`;
}
fileList.appendChild(div);
});
}
function updatePathDisplay() {
currentPathDisplay.textContent = `当前位置: ${currentPath === '/' ? '根目录' : currentPath}`;
}
function updateCapacity() {
const percent = Math.min((usedCapacity / maxCapacity) * 100, 100);
capacityDisplay.textContent = `已用空间: ${usedCapacity.toFixed(2)} MB / ${maxCapacity.toFixed(2)} MB (${percent.toFixed(1)}%)`;
progressBar.style.width = percent + '%';
progressBar.textContent = `${percent.toFixed(1)}%`;
}
window.enterFolder = function(path) {
currentPath = path;
pathStack.push(path);
updatePathDisplay();
renderFileList();
};
window.previewFile = function(fileName) {
const fileObj = fileSystem[currentPath].find(item => item.type === 'file' && item.file.name === fileName);
if (!fileObj) return alert('文件未找到');
const reader = new FileReader();
if (fileObj.file.type.includes('text')) {
reader.onload = e => {
const previewWindow = window.open('', '_blank');
previewWindow.document.write(`<pre>${e.target.result.slice(0, 2000)}</pre>`);
};
reader.readAsText(fileObj.file);
} else if (fileObj.file.type.includes('image')) {
const imgWin = window.open('', '_blank');
imgWin.document.write(`<img src="${URL.createObjectURL(fileObj.file)}" style="max-width:100%;">`);
} else {
alert('暂不支持该文件类型预览');
}
};
window.renameFile = function(fileName) {
const idx = fileSystem[currentPath].findIndex(item => item.type === 'file' && item.file.name === fileName);
if (idx === -1) return alert('文件未找到');
const newName = prompt('请输入新的文件名(包含扩展名):', fileName);
if (newName && newName.trim()) {
const oldFile = fileSystem[currentPath][idx].file;
const newFile = new File([oldFile], newName, { type: oldFile.type });
fileSystem[currentPath][idx].file = newFile;
renderFileList();
}
};
window.downloadFile = function(fileName) {
const fileObj = fileSystem[currentPath].find(item => item.type === 'file' && item.file.name === fileName);
if (!fileObj) return alert('文件未找到');
const url = URL.createObjectURL(fileObj.file);
const a = document.createElement('a');
a.href = url;
a.download = fileObj.file.name;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
};
window.removeFile = function(fileName) {
const idx = fileSystem[currentPath].findIndex(item => item.type === 'file' && item.file.name === fileName);
if (idx !== -1) {
usedCapacity -= parseFloat(fileSystem[currentPath][idx].sizeMB);
fileSystem[currentPath].splice(idx, 1);
updateCapacity();
renderFileList();
}
};
window.removeFolder = function(folderPath) {
if (confirm(`确定要删除文件夹 ${folderPath} 及其所有内容吗?`)) {
const folderSize = calculateFolderSize(folderPath);
usedCapacity -= folderSize;
Object.keys(fileSystem)
.filter(path => path.startsWith(folderPath))
.forEach(path => delete fileSystem[path]);
const parentPath = folderPath.substring(0, folderPath.lastIndexOf('/', folderPath.length - 2) + 1);
const idx = fileSystem[parentPath].findIndex(item => item.path === folderPath);
if (idx !== -1) fileSystem[parentPath].splice(idx, 1);
updateCapacity();
renderFileList();
}
};
function calculateFolderSize(path) {
let totalSize = 0;
Object.keys(fileSystem).forEach(key => {
if (key.startsWith(path)) {
fileSystem[key].forEach(item => {
if (item.type === 'file') {
totalSize += parseFloat(item.sizeMB);
}
});
}
});
return totalSize;
}
</script>
</body>
</html>
index.html
index.html