dev-server-cli
Version:
Quickly start a local service to implement testing and file transfer
168 lines (157 loc) • 5.54 kB
HTML
<!--
创建一个静态的视图文件,使用原生html+css+js实现,不使用任何框架
1. 获取数据:从"/api/list"接口获取目录列表,返回的数据分为文件和目录两种类型,带有一个isDir属性,表示是否是目录
2. 渲染数据:使用原生html+css+js实现,不使用任何框架,文件类型显示文件图标与名称,目录类型显示文件夹图标与名称并带有下划线
3. 添加点击事件:点击文件时加载子级目录,通过带上当前目录参数"/api/list?dir=当前目录"获取子级目录列表,点击文件时调用浏览器下载文件
4. 添加拖拽事件:拖拽文件时上传文件,上传文件时调用"/api/upload"接口,上传文件时带上当前目录参数"/api/upload?dir=当前目录"
-->
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>目录浏览器</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background: #f7f7f7;
}
#container {
max-width: 800px;
margin: 40px auto;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px #ccc;
padding: 24px;
}
h2 {
margin-top: 0;
}
ul {
list-style: none;
padding: 0;
}
li {
display: flex;
align-items: center;
padding: 8px 0;
cursor: pointer;
}
li.dir {
text-decoration: underline;
color: #0070f3;
}
li.file {
color: #333;
}
.icon {
width: 20px;
height: 20px;
margin-right: 10px;
}
#upload {
margin: 16px 0;
padding: 8px;
border: 1px dashed #aaa;
border-radius: 4px;
background: #fafafa;
text-align: center;
}
</style>
</head>
<body>
<div id="container">
<h2>目录浏览器</h2>
<div id="upload">拖拽文件到此处上传或 <span id="upload-btn" style="color:#0070f3;text-decoration:underline;cursor:pointer;">点击选择文件</span><input type="file" id="upload-input" style="display:none" multiple></div>
<ul id="list"></ul>
</div>
<script>
let currentDir = '';
function fetchList(dir = '') {
fetch(`/api/files${dir ? '?dir=' + encodeURIComponent(dir) : ''}`)
.then(res => res.json())
.then(data => renderList(data, dir));
}
function renderList(list, dir) {
currentDir = dir;
const ul = document.getElementById('list');
ul.innerHTML = '';
// 添加返回上级目录链接
if (dir) {
const liUp = document.createElement('li');
liUp.className = 'dir';
liUp.innerHTML = '<span class="icon">⬆️</span>...';
liUp.onclick = () => {
const parent = dir.lastIndexOf('/') > -1 ? dir.substring(0, dir.lastIndexOf('/')) : '';
fetchList(parent);
};
ul.appendChild(liUp);
}
list.forEach(item => {
const li = document.createElement('li');
li.className = item.isDir ? 'dir' : 'file';
li.innerHTML = item.isDir
? `<span class="icon">📁</span>${item.name}`
: `<span class="icon">📄</span>${item.name}`;
li.onclick = () => {
if (item.isDir) {
fetchList(dir ? dir + '/' + item.name : item.name);
} else {
window.location.href = item.downloadUrl || '/';
}
};
ul.appendChild(li);
});
}
document.getElementById('upload').addEventListener('dragover', e => {
e.preventDefault();
e.dataTransfer.dropEffect = 'copy';
});
document.getElementById('upload').addEventListener('drop', e => {
e.preventDefault();
const files = e.dataTransfer.files;
if (files.length === 0) return;
const formData = new FormData();
for (let i = 0; i < files.length; i++) {
formData.append('file', files[i]);
}
fetch(`/api/files?dir=${encodeURIComponent(currentDir)}`, {
method: 'POST',
body: formData
}).then(res => {
if (res.ok) {
fetchList(currentDir);
} else {
alert(`上传失败`);
}
});
});
// 支持点击选择文件上传
document.getElementById('upload-btn').addEventListener('click', function() {
document.getElementById('upload-input').click();
});
document.getElementById('upload-input').addEventListener('change', function(e) {
const files = e.target.files;
if (files.length === 0) return;
const formData = new FormData();
for (let i = 0; i < files.length; i++) {
formData.append('file', files[i]);
}
fetch(`/api/files?dir=${encodeURIComponent(currentDir)}`, {
method: 'POST',
body: formData
}).then(res => {
if (res.ok) {
fetchList(currentDir);
} else {
alert('上传失败');
}
});
// 清空 input,避免同名文件无法重复上传
e.target.value = '';
});
fetchList();
</script>
</body>
</html>