Files
kevin c16a8dfbc4 feat: 门户网站初始提交
- Go + Gin + html/template 服务端渲染
- 主页:Google 风格搜索框 + 导航卡片
- 后台:卡片 CRUD、搜索引擎配置、主页背景/标题配置
- 图片上传:支持 jpg/jpeg/png/gif,自动压缩,缩略图参数 ?thumb=1
- 安全:登录日志、修改密码、IP 自动封禁、IP 白名单
- 访问统计:主页访问/卡片点击/搜索追踪、实时流量、IP 统计
- SQLite 存储(modernc.org/sqlite,纯 Go)
- 内存 Session + bcrypt 密码哈希
2026-05-28 13:54:07 +08:00

141 lines
4.4 KiB
JavaScript

/**
* upload.js — Generic file upload handler for Portal admin pages.
*
* Usage:
* setupUpload(inputSelector, uploadType)
* - inputSelector: CSS selector for the target input field
* - uploadType: "icon" or "background" — determines thumbnail dimensions
*
* Adds a hidden file input and a visible "上传图片" button next to the target input.
* After successful upload, the target input value is set to the returned URL.
*/
(function () {
"use strict";
/**
* Sets up an upload button next to the specified input field.
* @param {string} inputSelector - CSS selector for the input to fill with the URL
* @param {string} uploadType - "icon" or "background"
*/
function setupUpload(inputSelector, uploadType) {
var input = document.querySelector(inputSelector);
if (!input) return;
// Create container for the upload button group
var wrapper = document.createElement("div");
wrapper.className = "upload-btn-wrapper";
// Create the visible upload button
var btn = document.createElement("button");
btn.type = "button";
btn.className = "upload-btn";
btn.textContent = "上传图片";
// Create the hidden file input
var fileInput = document.createElement("input");
fileInput.type = "file";
fileInput.accept = "image/jpeg,image/png,image/gif";
fileInput.style.display = "none";
// Create status message element
var status = document.createElement("span");
status.className = "upload-status";
// Insert wrapper after the input
input.parentNode.insertBefore(wrapper, input.nextSibling);
wrapper.appendChild(btn);
wrapper.appendChild(fileInput);
wrapper.appendChild(status);
// Click button -> open file dialog
btn.addEventListener("click", function () {
fileInput.click();
});
// File selected -> upload
fileInput.addEventListener("change", function () {
if (fileInput.files.length === 0) return;
var file = fileInput.files[0];
// Validate file size (5MB)
if (file.size > 5 * 1024 * 1024) {
status.textContent = "文件太大,最大允许 5MB";
status.className = "upload-status upload-error";
return;
}
// Validate file type
var validTypes = ["image/jpeg", "image/png", "image/gif"];
if (validTypes.indexOf(file.type) === -1) {
status.textContent = "不支持的文件格式";
status.className = "upload-status upload-error";
return;
}
// Start upload
btn.disabled = true;
btn.textContent = "上传中...";
status.textContent = "";
status.className = "upload-status";
var formData = new FormData();
formData.append("file", file);
formData.append("type", uploadType);
var xhr = new XMLHttpRequest();
xhr.open("POST", "/admin/upload", true);
xhr.onload = function () {
btn.disabled = false;
btn.textContent = "上传图片";
if (xhr.status === 200) {
try {
var resp = JSON.parse(xhr.responseText);
if (resp.url) {
input.value = resp.url;
status.textContent = "上传成功!";
status.className = "upload-status upload-success";
// If there's a preview element, update it
var preview = input.parentNode.querySelector(".upload-preview");
if (preview) {
preview.src = resp.url + "?thumb=1";
preview.style.display = "block";
}
} else {
status.textContent = resp.error || "上传失败";
status.className = "upload-status upload-error";
}
} catch (e) {
status.textContent = "上传失败";
status.className = "upload-status upload-error";
}
} else {
try {
var resp = JSON.parse(xhr.responseText);
status.textContent = resp.error || "上传失败";
} catch (e) {
status.textContent = "上传失败 (" + xhr.status + ")";
}
status.className = "upload-status upload-error";
}
};
xhr.onerror = function () {
btn.disabled = false;
btn.textContent = "上传图片";
status.textContent = "网络错误";
status.className = "upload-status upload-error";
};
xhr.send(formData);
});
}
// Expose globally
window.setupUpload = setupUpload;
})();