You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
star/httpserver/template.html

479 lines
16 KiB
HTML

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!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>