Upload 3 files
Browse files- static/app.js +64 -9
- static/index.html +14 -2
- static/style.css +9 -0
static/app.js
CHANGED
|
@@ -149,6 +149,8 @@ function cacheDOM() {
|
|
| 149 |
DOM.filterExtList = $("#filter-ext-list");
|
| 150 |
DOM.filterMinSize = $("#filter-min-size");
|
| 151 |
DOM.filterMaxSize = $("#filter-max-size");
|
|
|
|
|
|
|
| 152 |
DOM.closeFiltersBtn = $("#close-filters-btn");
|
| 153 |
DOM.folderSelectAll = $("#folder-select-all");
|
| 154 |
DOM.folderDeselectAll = $("#folder-deselect-all");
|
|
@@ -373,8 +375,8 @@ function syncStateToURL(replace = true) {
|
|
| 373 |
if (DOM.sortSelect.value !== "relevance") {
|
| 374 |
params.set("sort", DOM.sortSelect.value);
|
| 375 |
}
|
| 376 |
-
if (STATE.filterMinSize !== null) params.set("min_size", STATE.filterMinSize);
|
| 377 |
-
if (STATE.filterMaxSize !== null) params.set("max_size", STATE.filterMaxSize);
|
| 378 |
if (!STATE.searchFolders) params.set("search_folders", "false");
|
| 379 |
if (STATE.exact) params.set("exact", "1");
|
| 380 |
|
|
@@ -406,12 +408,30 @@ function syncURLToState() {
|
|
| 406 |
DOM.sortSelect.value = sort;
|
| 407 |
|
| 408 |
const minSize = params.get("min_size");
|
| 409 |
-
|
| 410 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 411 |
|
| 412 |
const maxSize = params.get("max_size");
|
| 413 |
-
|
| 414 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 415 |
|
| 416 |
const searchFolders = params.get("search_folders");
|
| 417 |
STATE.searchFolders = searchFolders !== "false";
|
|
@@ -1229,6 +1249,7 @@ function clearAllFilters() {
|
|
| 1229 |
STATE.filterMinSize = null; STATE.filterMaxSize = null;
|
| 1230 |
STATE.page = 1; STATE.results = [];
|
| 1231 |
DOM.filterMinSize.value = ""; DOM.filterMaxSize.value = "";
|
|
|
|
| 1232 |
renderFilters(); doSearch(); showToast("已清空所有筛选条件");
|
| 1233 |
syncStateToURL(true);
|
| 1234 |
}
|
|
@@ -1337,9 +1358,43 @@ async function init() {
|
|
| 1337 |
DOM.emptyRandomBtn.addEventListener('click', randomBook);
|
| 1338 |
|
| 1339 |
let sizeTimer;
|
| 1340 |
-
const
|
| 1341 |
-
|
| 1342 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1343 |
|
| 1344 |
DOM.extSelectAll.addEventListener("click", () => { STATE.filterExtensions = (STATE.extensionList || []).map(e => e.name); STATE.page = 1; STATE.results = []; doSearch(); renderExtensionFilter(); });
|
| 1345 |
DOM.extDeselectAll.addEventListener("click", () => {
|
|
|
|
| 149 |
DOM.filterExtList = $("#filter-ext-list");
|
| 150 |
DOM.filterMinSize = $("#filter-min-size");
|
| 151 |
DOM.filterMaxSize = $("#filter-max-size");
|
| 152 |
+
DOM.filterMinUnit = $("#filter-min-unit");
|
| 153 |
+
DOM.filterMaxUnit = $("#filter-max-unit");
|
| 154 |
DOM.closeFiltersBtn = $("#close-filters-btn");
|
| 155 |
DOM.folderSelectAll = $("#folder-select-all");
|
| 156 |
DOM.folderDeselectAll = $("#folder-deselect-all");
|
|
|
|
| 375 |
if (DOM.sortSelect.value !== "relevance") {
|
| 376 |
params.set("sort", DOM.sortSelect.value);
|
| 377 |
}
|
| 378 |
+
if (STATE.filterMinSize !== null) params.set("min_size", fmtSizeUrl(STATE.filterMinSize));
|
| 379 |
+
if (STATE.filterMaxSize !== null) params.set("max_size", fmtSizeUrl(STATE.filterMaxSize));
|
| 380 |
if (!STATE.searchFolders) params.set("search_folders", "false");
|
| 381 |
if (STATE.exact) params.set("exact", "1");
|
| 382 |
|
|
|
|
| 408 |
DOM.sortSelect.value = sort;
|
| 409 |
|
| 410 |
const minSize = params.get("min_size");
|
| 411 |
+
if (minSize) {
|
| 412 |
+
var parsed = parseSizeStr(minSize);
|
| 413 |
+
STATE.filterMinSize = parsed;
|
| 414 |
+
var disp = bytesToDisplay(parsed);
|
| 415 |
+
DOM.filterMinSize.value = disp.value;
|
| 416 |
+
DOM.filterMinUnit.value = disp.unit;
|
| 417 |
+
} else {
|
| 418 |
+
STATE.filterMinSize = null;
|
| 419 |
+
DOM.filterMinSize.value = "";
|
| 420 |
+
DOM.filterMinUnit.value = "MB";
|
| 421 |
+
}
|
| 422 |
|
| 423 |
const maxSize = params.get("max_size");
|
| 424 |
+
if (maxSize) {
|
| 425 |
+
var parsedMx = parseSizeStr(maxSize);
|
| 426 |
+
STATE.filterMaxSize = parsedMx;
|
| 427 |
+
var dispMx = bytesToDisplay(parsedMx);
|
| 428 |
+
DOM.filterMaxSize.value = dispMx.value;
|
| 429 |
+
DOM.filterMaxUnit.value = dispMx.unit;
|
| 430 |
+
} else {
|
| 431 |
+
STATE.filterMaxSize = null;
|
| 432 |
+
DOM.filterMaxSize.value = "";
|
| 433 |
+
DOM.filterMaxUnit.value = "MB";
|
| 434 |
+
}
|
| 435 |
|
| 436 |
const searchFolders = params.get("search_folders");
|
| 437 |
STATE.searchFolders = searchFolders !== "false";
|
|
|
|
| 1249 |
STATE.filterMinSize = null; STATE.filterMaxSize = null;
|
| 1250 |
STATE.page = 1; STATE.results = [];
|
| 1251 |
DOM.filterMinSize.value = ""; DOM.filterMaxSize.value = "";
|
| 1252 |
+
DOM.filterMinUnit.value = "MB"; DOM.filterMaxUnit.value = "MB";
|
| 1253 |
renderFilters(); doSearch(); showToast("已清空所有筛选条件");
|
| 1254 |
syncStateToURL(true);
|
| 1255 |
}
|
|
|
|
| 1358 |
DOM.emptyRandomBtn.addEventListener('click', randomBook);
|
| 1359 |
|
| 1360 |
let sizeTimer;
|
| 1361 |
+
const sizeInputToBytes = (input, unitSelect) => {
|
| 1362 |
+
var val = parseFloat(input.value);
|
| 1363 |
+
if (isNaN(val) || val < 0) return null;
|
| 1364 |
+
var unit = unitSelect.value;
|
| 1365 |
+
if (unit === "KB") val *= 1024;
|
| 1366 |
+
else if (unit === "MB") val *= 1048576;
|
| 1367 |
+
else if (unit === "GB") val *= 1073741824;
|
| 1368 |
+
return Math.round(val);
|
| 1369 |
+
};
|
| 1370 |
+
const bytesToDisplay = (bytes) => {
|
| 1371 |
+
if (bytes === null || bytes === undefined || bytes === 0) return { value: "", unit: "MB" };
|
| 1372 |
+
if (bytes >= 1073741824) return { value: (bytes / 1073741824).toFixed(2).replace(/\.?0+$/, ""), unit: "GB" };
|
| 1373 |
+
if (bytes >= 1048576) return { value: (bytes / 1048576).toFixed(1).replace(/\.0$/, ""), unit: "MB" };
|
| 1374 |
+
if (bytes >= 1024) return { value: (bytes / 1024).toFixed(1).replace(/\.0$/, ""), unit: "KB" };
|
| 1375 |
+
return { value: String(bytes), unit: "B" };
|
| 1376 |
+
};
|
| 1377 |
+
const fmtSizeUrl = (bytes) => {
|
| 1378 |
+
if (bytes === null || bytes === undefined) return null;
|
| 1379 |
+
var d = bytesToDisplay(bytes);
|
| 1380 |
+
return d.value + d.unit;
|
| 1381 |
+
};
|
| 1382 |
+
const parseSizeStr = (str) => {
|
| 1383 |
+
if (!str) return null;
|
| 1384 |
+
var m = String(str).match(/^([\d.]+)\s*(GB|MB|KB|B)?$/i);
|
| 1385 |
+
if (!m) return parseInt(str) || null;
|
| 1386 |
+
var val = parseFloat(m[1]);
|
| 1387 |
+
var unit = (m[2] || "B").toUpperCase();
|
| 1388 |
+
if (unit === "GB") val *= 1073741824;
|
| 1389 |
+
else if (unit === "MB") val *= 1048576;
|
| 1390 |
+
else if (unit === "KB") val *= 1024;
|
| 1391 |
+
return Math.round(val);
|
| 1392 |
+
};
|
| 1393 |
+
const applySizeFilter = () => { clearTimeout(sizeTimer); sizeTimer = setTimeout(() => { STATE.filterMinSize = sizeInputToBytes(DOM.filterMinSize, DOM.filterMinUnit); STATE.filterMaxSize = sizeInputToBytes(DOM.filterMaxSize, DOM.filterMaxUnit); STATE.page = 1; STATE.results = []; doSearch(); }, 500); };
|
| 1394 |
+
DOM.filterMinSize.addEventListener("input", applySizeFilter);
|
| 1395 |
+
DOM.filterMaxSize.addEventListener("input", applySizeFilter);
|
| 1396 |
+
DOM.filterMinUnit.addEventListener("change", applySizeFilter);
|
| 1397 |
+
DOM.filterMaxUnit.addEventListener("change", applySizeFilter);
|
| 1398 |
|
| 1399 |
DOM.extSelectAll.addEventListener("click", () => { STATE.filterExtensions = (STATE.extensionList || []).map(e => e.name); STATE.page = 1; STATE.results = []; doSearch(); renderExtensionFilter(); });
|
| 1400 |
DOM.extDeselectAll.addEventListener("click", () => {
|
static/index.html
CHANGED
|
@@ -214,9 +214,21 @@
|
|
| 214 |
<div class="filter-section">
|
| 215 |
<div class="filter-section-title">文件大小</div>
|
| 216 |
<div class="filter-size-row">
|
| 217 |
-
<input type="number" id="filter-min-size" class="size-input" placeholder="最小
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 218 |
<span class="size-sep">—</span>
|
| 219 |
-
<input type="number" id="filter-max-size" class="size-input" placeholder="最大
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 220 |
</div>
|
| 221 |
</div>
|
| 222 |
</div>
|
|
|
|
| 214 |
<div class="filter-section">
|
| 215 |
<div class="filter-section-title">文件大小</div>
|
| 216 |
<div class="filter-size-row">
|
| 217 |
+
<input type="number" id="filter-min-size" class="size-input" placeholder="最小" min="0" step="0.1">
|
| 218 |
+
<select id="filter-min-unit" class="size-unit">
|
| 219 |
+
<option value="B">B</option>
|
| 220 |
+
<option value="KB">KB</option>
|
| 221 |
+
<option value="MB" selected>MB</option>
|
| 222 |
+
<option value="GB">GB</option>
|
| 223 |
+
</select>
|
| 224 |
<span class="size-sep">—</span>
|
| 225 |
+
<input type="number" id="filter-max-size" class="size-input" placeholder="最大" min="0" step="0.1">
|
| 226 |
+
<select id="filter-max-unit" class="size-unit">
|
| 227 |
+
<option value="B">B</option>
|
| 228 |
+
<option value="KB">KB</option>
|
| 229 |
+
<option value="MB" selected>MB</option>
|
| 230 |
+
<option value="GB">GB</option>
|
| 231 |
+
</select>
|
| 232 |
</div>
|
| 233 |
</div>
|
| 234 |
</div>
|
static/style.css
CHANGED
|
@@ -1030,6 +1030,15 @@ body.light .left-sidebar.expanded-wide .browser-item:nth-child(even) {
|
|
| 1030 |
.size-input:focus {
|
| 1031 |
border-color: var(--primary);
|
| 1032 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1033 |
|
| 1034 |
.size-sep {
|
| 1035 |
font-size: 12px;
|
|
|
|
| 1030 |
.size-input:focus {
|
| 1031 |
border-color: var(--primary);
|
| 1032 |
}
|
| 1033 |
+
.size-unit {
|
| 1034 |
+
font-size: 12px;
|
| 1035 |
+
padding: 6px 4px;
|
| 1036 |
+
border: 1px solid var(--outline-variant);
|
| 1037 |
+
border-radius: var(--radius-sm);
|
| 1038 |
+
background: var(--surface);
|
| 1039 |
+
color: var(--on-surface);
|
| 1040 |
+
cursor: pointer;
|
| 1041 |
+
}
|
| 1042 |
|
| 1043 |
.size-sep {
|
| 1044 |
font-size: 12px;
|