479 lines
16 KiB
HTML
479 lines
16 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh_CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>B612 Http Server {{ .IdxTitle }}</title>
|
||
<style>
|
||
* {
|
||
-webkit-box-sizing: border-box;
|
||
-moz-box-sizing: border-box;
|
||
box-sizing: border-box;
|
||
}
|
||
body {
|
||
background-color: #f5f5f5;
|
||
font-family: Arial, sans-serif;
|
||
margin: 0;
|
||
padding: 0;
|
||
display: -webkit-box;
|
||
display: -moz-box;
|
||
display: -ms-flexbox;
|
||
display: -webkit-flex;
|
||
display: flex;
|
||
-webkit-flex-direction: column;
|
||
-moz-flex-direction: column;
|
||
-ms-flex-direction: column;
|
||
flex-direction: column;
|
||
height: 100vh;
|
||
position: relative; /* 为了使背景图片固定 */
|
||
}
|
||
.background {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
z-index: -1;
|
||
{{ .Photo }} /* 这里的背景图片 URL 是动态插入的 */
|
||
background-size: cover;
|
||
opacity: 0.5; /* 调整透明度 */
|
||
}
|
||
@media screen and (max-width: 768px) {
|
||
.background {
|
||
{{ .MobilePhoto }}
|
||
background-size: cover;
|
||
}
|
||
}
|
||
|
||
.container {
|
||
-webkit-flex: 1;
|
||
-moz-flex: 1;
|
||
-ms-flex: 1;
|
||
flex: 1;
|
||
width: 50%;
|
||
max-width: 1920px;
|
||
margin: 0 auto;
|
||
padding: 24px;
|
||
display: -webkit-box;
|
||
display: -moz-box;
|
||
display: -ms-flexbox;
|
||
display: -webkit-flex;
|
||
display: flex;
|
||
-webkit-flex-direction: column;
|
||
-moz-flex-direction: column;
|
||
-ms-flex-direction: column;
|
||
flex-direction: column;
|
||
background: rgba(245, 245, 245, 0.5); /* 添加一个半透明背景层 */
|
||
}
|
||
h1 {
|
||
text-align: center;
|
||
margin-bottom: 24px;
|
||
}
|
||
.table-container {
|
||
-webkit-flex: 1;
|
||
-moz-flex: 1;
|
||
-ms-flex: 1;
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
overflow-x: hidden;
|
||
position: relative;
|
||
}
|
||
table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
margin-top: 24px;
|
||
table-layout: fixed; /* 确保表格单元格宽度固定 */
|
||
}
|
||
th, td {
|
||
padding: 12px;
|
||
text-align: left;
|
||
border-bottom: 1px solid #ddd;
|
||
white-space: normal; /* 允许换行 */
|
||
background: rgba(245, 245, 245, 0.5); /* 设置表格单元格背景为半透明 */
|
||
}
|
||
th[data-sort]:after {
|
||
content: "▲";
|
||
display: inline-block;
|
||
height: 20px;
|
||
width: 20px;
|
||
margin-left: 10px;
|
||
vertical-align: middle;
|
||
opacity: 0.3;
|
||
-webkit-transition: all 0.2s ease-in-out;
|
||
-moz-transition: all 0.2s ease-in-out;
|
||
-ms-transition: all 0.2s ease-in-out;
|
||
-o-transition: all 0.2s ease-in-out;
|
||
transition: all 0.2s ease-in-out;
|
||
}
|
||
th.asc:after {
|
||
content: "▲";
|
||
opacity: 1;
|
||
}
|
||
th.desc:after {
|
||
content: "▼";
|
||
opacity: 1;
|
||
}
|
||
th:hover:after {
|
||
opacity: 1;
|
||
}
|
||
.filename {
|
||
color: #007bff;
|
||
text-decoration: underline;
|
||
display: block;
|
||
max-width: 600px; /* 调整你想要的最大宽度 */
|
||
word-wrap: break-word; /* 确保文件名能够换行 */
|
||
}
|
||
.filename:hover {
|
||
color: #0056b3;
|
||
}
|
||
tr:hover {
|
||
background-color: rgba(241, 241, 241, 0.5); /* 设置鼠标悬停效果的背景为半透明 */
|
||
}
|
||
.filetype {
|
||
text-transform: uppercase;
|
||
}
|
||
@media screen and (max-width: 600px) {
|
||
table {
|
||
font-size: 14px;
|
||
}
|
||
}
|
||
@media (orientation: portrait) and (max-width: 1024px) {
|
||
.container {
|
||
width: 60%;
|
||
}
|
||
}
|
||
@media (max-width: 400px) {
|
||
.container {
|
||
width: 80%;
|
||
}
|
||
}
|
||
thead th {
|
||
position: -webkit-sticky;
|
||
position: -moz-sticky;
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 1;
|
||
background: rgba(245, 245, 245, 0.5); /* 设置表头背景为半透明 */
|
||
}
|
||
tbody td:first-child,
|
||
thead th:first-child {
|
||
position: -webkit-sticky;
|
||
position: -moz-sticky;
|
||
position: sticky;
|
||
left: 0;
|
||
z-index: 1;
|
||
background: rgba(245, 245, 245, 0.5); /* 设置第一列背景为半透明 */
|
||
}
|
||
hr {
|
||
width: 100%;
|
||
}
|
||
|
||
.search-container {
|
||
display: -webkit-box;
|
||
display: -moz-box;
|
||
display: -ms-flexbox;
|
||
display: -webkit-flex;
|
||
display: flex;
|
||
-webkit-justify-content: center;
|
||
-moz-justify-content: center;
|
||
-ms-justify-content: center;
|
||
justify-content: center;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.search-container input {
|
||
padding: 10px;
|
||
width: 80%;
|
||
max-width: 400px;
|
||
font-size: 16px;
|
||
border: 1px solid #ddd;
|
||
border-radius: 4px;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="background"></div>
|
||
<div class="container">
|
||
<h1>B612 Http Server - {{ .Version }}</h1>
|
||
<hr />
|
||
<h2>{{ .Idx }}</h2>
|
||
{{ .Upload }}
|
||
<div class="search-container">
|
||
<input type="text" id="search-box" placeholder="Search for a file..." />
|
||
</div>
|
||
<div class="table-container" id="table-container">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th data-sort="name">Name</th>
|
||
<th data-sort="modified">Modified</th>
|
||
<th data-sort="size">Size</th>
|
||
<th data-sort="type">Type</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="table-content"><!-- table content is dynamically filled by JavaScript -->
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<hr />
|
||
<h2 style="text-align: center;">B612.Me © Apache 2.0 License</h2>
|
||
</div>
|
||
|
||
<!-- Context menu for copying file details -->
|
||
<div id="context-menu" style="display:none; position: absolute; z-index: 1000; background: white; border: 1px solid #ccc; padding: 5px;">
|
||
<ul style="list-style: none; margin: 0; padding: 0;">
|
||
<li id="copy-filename" style="padding: 5px; cursor: pointer;">复制文件名</li>
|
||
<li id="copy-link" style="padding: 5px; cursor: pointer;">复制文件链接地址</li>
|
||
<li id="copy-size-bytes" style="padding: 5px; cursor: pointer;">复制文件大小(按字节)</li>
|
||
<li id="copy-size-display" style="padding: 5px; cursor: pointer;">复制文件大小(按显示)</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<script>
|
||
// 初始化内容
|
||
var dataRows = {{ .Data }};
|
||
|
||
function loadTableContent(dataRows) {
|
||
var tableContent = document.getElementById('table-content');
|
||
if (!tableContent) return;
|
||
|
||
var html = '';
|
||
dataRows.forEach(function(row) {
|
||
html += '<tr>';
|
||
html += '<td><a class="filename" href="' + row.attr + '">' + row.name + '</a></td>';
|
||
html += '<td>' + row.modified + '</td>';
|
||
html += '<td title="' + row.size + ' bytes">' + formatSize(row.size) + '</td>';
|
||
html += '<td class="filetype">' + row.type + '</td>';
|
||
html += '</tr>';
|
||
});
|
||
|
||
tableContent.innerHTML = html;
|
||
}
|
||
function renderRows(rows) {
|
||
var tableContent = document.getElementById('table-content');
|
||
var fragment = document.createDocumentFragment();
|
||
rows.forEach(function(row) {
|
||
var tr = document.createElement('tr');
|
||
tr.innerHTML += '<td><a class="filename" href="' + row.attr + '">' + row.name + '</a></td>';
|
||
tr.innerHTML += '<td>' + row.modified + '</td>';
|
||
var formattedSize = formatSize(row.size);
|
||
tr.innerHTML += '<td title="' + row.size + ' bytes">' + formattedSize + '</td>';
|
||
tr.innerHTML += '<td class="filetype">' + row.type + '</td>';
|
||
fragment.appendChild(tr);
|
||
});
|
||
tableContent.innerHTML = ''; // 清空现有内容
|
||
tableContent.appendChild(fragment);
|
||
}
|
||
|
||
function chunkedRenderRows() {
|
||
var chunkSize = 50;
|
||
var currentIndex = 0;
|
||
|
||
function renderChunk() {
|
||
var fragment = document.createDocumentFragment();
|
||
for (var i = currentIndex; i < currentIndex + chunkSize && i < dataRows.length; i++) {
|
||
var row = dataRows[i];
|
||
if (row.name === '..' && i !== 0) continue;
|
||
var tr = document.createElement('tr');
|
||
tr.innerHTML += '<td><a class="filename" href="' + row.attr + '">' + row.name + '</a></td>';
|
||
tr.innerHTML += '<td>' + row.modified + '</td>';
|
||
var formattedSize = formatSize(row.size);
|
||
tr.innerHTML += '<td title="' + row.size + ' bytes">' + formattedSize + '</td>';
|
||
tr.innerHTML += '<td class="filetype">' + row.type + '</td>';
|
||
fragment.appendChild(tr);
|
||
}
|
||
document.getElementById('table-content').appendChild(fragment);
|
||
currentIndex += chunkSize;
|
||
if (currentIndex < dataRows.length) {
|
||
requestAnimationFrame(renderChunk);
|
||
}
|
||
}
|
||
|
||
// 清空现有内容
|
||
document.getElementById('table-content').innerHTML = '';
|
||
// 开始分块渲染
|
||
requestAnimationFrame(renderChunk);
|
||
}
|
||
|
||
function parseSize(size) {
|
||
var units = { 'KB': 1024, 'MB': 1024 * 1024, 'GB': 1024 * 1024 * 1024 };
|
||
var match = size.match(/(\d+\.?\d*)\s*(KB|MB|GB)/i);
|
||
|
||
if (match) {
|
||
return parseFloat(match[1]) * (units[match[2].toUpperCase()] || 1);
|
||
}
|
||
return parseInt(size, 10);
|
||
}
|
||
|
||
function formatSize(size) {
|
||
if (size < 0) return "-";
|
||
if (size < 1024) return size + ' B';
|
||
else if (size < 1024 * 1024) return (size / 1024).toFixed(2) + ' KB';
|
||
else if (size < 1024 * 1024 * 1024) return (size / (1024 * 1024)).toFixed(2) + ' MB';
|
||
else return (size / (1024 * 1024 * 1024)).toFixed(2) + ' GB';
|
||
}
|
||
|
||
function sortTable(th, n, initial) {
|
||
var direction = th.classList.contains('asc') && !initial ? 'desc' : 'asc';
|
||
dataRows.sort(function(a, b) {
|
||
// 检查 'name' 字段以确保 '..' 始终在第一位
|
||
if (a.name === '..') return -1;
|
||
if (b.name === '..') return 1;
|
||
|
||
var x = Object.values(a)[n];
|
||
var y = Object.values(b)[n];
|
||
|
||
if (n === 1) { // modified column
|
||
// 解析日期字符串
|
||
x = new Date(a.modified);
|
||
y = new Date(b.modified);
|
||
} else if (n === 2) { // size column
|
||
x = a.size;
|
||
y = b.size;
|
||
}
|
||
|
||
return direction === 'asc' ?
|
||
(x < y ? -1 : x > y ? 1 : 0) :
|
||
(x > y ? -1 : x < y ? 1 : 0);
|
||
});
|
||
|
||
th.classList.toggle('asc', direction === 'asc' && !initial);
|
||
th.classList.toggle('desc', direction === 'desc' && !initial);
|
||
updateSortIcons(th);
|
||
renderRows(dataRows);
|
||
}
|
||
|
||
|
||
function updateSortIcons(th) {
|
||
var ths = document.querySelectorAll('thead th[data-sort]');
|
||
ths.forEach(function(header) {
|
||
if (header !== th) {
|
||
header.classList.remove('asc');
|
||
header.classList.remove('desc');
|
||
}
|
||
});
|
||
}
|
||
|
||
// 初次加载时按 Name 列进行升序排序
|
||
function initialSort() {
|
||
var nameHeader = document.querySelector('th[data-sort="name"]');
|
||
if (nameHeader) {
|
||
nameHeader.classList.add('asc'); // 直接设置为升序状态
|
||
sortTable(nameHeader, 0, true); // 传递一个参数表示这是初始排序
|
||
}
|
||
}
|
||
|
||
function isIE() {
|
||
return window.navigator.userAgent.indexOf("MSIE ") > -1 || navigator.userAgent.indexOf("Trident/") > -1;
|
||
}
|
||
|
||
|
||
|
||
if (!isIE()) {
|
||
document.addEventListener('DOMContentLoaded', function(event) {
|
||
var ths = document.querySelectorAll('thead th[data-sort]');
|
||
ths.forEach(function(th, i) {
|
||
th.addEventListener('click', function() {
|
||
sortTable(th, i);
|
||
});
|
||
});
|
||
|
||
// 使用 chunkedRenderRows 进行初始渲染, 然后进行默认排序
|
||
renderRows(dataRows);
|
||
|
||
// 初次加载时按 Name 列进行升序排序
|
||
initialSort();
|
||
|
||
// 初始化右键菜单
|
||
initializeContextMenu();
|
||
});
|
||
// 处理搜索框输入事件
|
||
document.getElementById('search-box').addEventListener('input', function (e) {
|
||
var searchText = e.target.value.toLowerCase();
|
||
var filteredRows = dataRows.filter(function (row) {
|
||
return row.name.toLowerCase().includes(searchText);
|
||
});
|
||
renderRows(filteredRows);
|
||
});
|
||
}else{
|
||
loadTableContent(dataRows);
|
||
// 禁用搜索框
|
||
var searchBox = document.getElementById('search-box');
|
||
if (searchBox) {
|
||
searchBox.disabled = true;
|
||
searchBox.placeholder = "IE浏览器不受支持,大部分功能受限";
|
||
}
|
||
}
|
||
|
||
function initializeContextMenu() {
|
||
var contextMenu = document.getElementById('context-menu');
|
||
document.addEventListener('contextmenu', function(e) {
|
||
if (e.target.classList.contains('filetype')) {
|
||
e.preventDefault();
|
||
var row = e.target.closest('tr');
|
||
var fileNameToCopy = row.querySelector('.filename').textContent;
|
||
var linkToCopy = row.querySelector('.filename').href;
|
||
var byteSizeToCopy = row.querySelector('td[title]') ? row.querySelector('td[title]').getAttribute('title') : '';
|
||
var displaySizeToCopy = row.querySelector('td[title]') ? row.querySelector('td[title]').textContent : '';
|
||
|
||
contextMenu.style.display = 'block';
|
||
contextMenu.style.top = e.pageY + 'px';
|
||
contextMenu.style.left = e.pageX + 'px';
|
||
contextMenu.setAttribute('data-filename', fileNameToCopy);
|
||
contextMenu.setAttribute('data-link', linkToCopy);
|
||
contextMenu.setAttribute('data-size-bytes', byteSizeToCopy);
|
||
contextMenu.setAttribute('data-size-display', displaySizeToCopy);
|
||
} else {
|
||
contextMenu.style.display = 'none';
|
||
}
|
||
});
|
||
|
||
document.addEventListener('click', function() {
|
||
contextMenu.style.display = 'none';
|
||
});
|
||
|
||
document.getElementById('copy-filename').addEventListener('click', function() {
|
||
var fileName = contextMenu.getAttribute('data-filename');
|
||
if (fileName) {
|
||
copyToClipboard(fileName);
|
||
}
|
||
contextMenu.style.display = 'none';
|
||
});
|
||
|
||
document.getElementById('copy-link').addEventListener('click', function() {
|
||
var fileLink = contextMenu.getAttribute('data-link');
|
||
if (fileLink) {
|
||
copyToClipboard(fileLink);
|
||
}
|
||
contextMenu.style.display = 'none';
|
||
});
|
||
|
||
document.getElementById('copy-size-bytes').addEventListener('click', function() {
|
||
var fileSizeBytes = contextMenu.getAttribute('data-size-bytes');
|
||
if (fileSizeBytes) {
|
||
copyToClipboard(fileSizeBytes);
|
||
}
|
||
contextMenu.style.display = 'none';
|
||
});
|
||
|
||
document.getElementById('copy-size-display').addEventListener('click', function() {
|
||
var fileSizeDisplay = contextMenu.getAttribute('data-size-display');
|
||
if (fileSizeDisplay) {
|
||
copyToClipboard(fileSizeDisplay);
|
||
}
|
||
contextMenu.style.display = 'none';
|
||
});
|
||
}
|
||
|
||
function copyToClipboard(text) {
|
||
var textarea = document.createElement('textarea');
|
||
textarea.value = text;
|
||
document.body.appendChild(textarea);
|
||
textarea.select();
|
||
document.execCommand('copy');
|
||
document.body.removeChild(textarea);
|
||
}
|
||
|
||
</script>
|
||
</body>
|
||
</html>
|