(function () {
|
var config = Object.assign({
|
apiBaseUrl: "http://localhost:9098"
|
}, window.VISITOR_LOGIN_CONFIG || {});
|
|
var MAX_PACKAGE_SIZE = 200 * 1024 * 1024;
|
var state = {
|
pageNum: 1,
|
pageSize: 20,
|
rows: [],
|
mode: "add",
|
packageFile: null
|
};
|
|
init();
|
|
function init() {
|
if (!getToken()) {
|
redirectToLogin();
|
return;
|
}
|
bindLayoutActions();
|
bindPageActions();
|
applyUserInfo(readLoginUser());
|
loadProfileName();
|
loadVersions();
|
}
|
|
function bindLayoutActions() {
|
var userMenuButton = document.getElementById("userMenuButton");
|
var userDropdown = document.getElementById("userDropdown");
|
var fullscreenButton = document.querySelector(".icon-button");
|
|
userMenuButton.addEventListener("click", function (event) {
|
event.stopPropagation();
|
var hidden = userDropdown.hidden;
|
userDropdown.hidden = !hidden;
|
userMenuButton.setAttribute("aria-expanded", String(hidden));
|
});
|
|
userDropdown.addEventListener("click", function (event) {
|
var action = event.target.getAttribute("data-action");
|
if (!action) {
|
return;
|
}
|
userDropdown.hidden = true;
|
userMenuButton.setAttribute("aria-expanded", "false");
|
if (action === "home") {
|
window.location.href = "./dashboard.html";
|
} else if (action === "logout") {
|
logout();
|
}
|
});
|
|
document.addEventListener("click", function () {
|
userDropdown.hidden = true;
|
userMenuButton.setAttribute("aria-expanded", "false");
|
});
|
|
fullscreenButton.addEventListener("click", function () {
|
if (!document.fullscreenElement && document.documentElement.requestFullscreen) {
|
document.documentElement.requestFullscreen();
|
} else if (document.exitFullscreen) {
|
document.exitFullscreen();
|
}
|
});
|
}
|
|
function bindPageActions() {
|
var uploadDrop = document.getElementById("uploadDrop");
|
var packageFile = document.getElementById("packageFile");
|
var versionNoInput = document.getElementById("versionNo");
|
|
document.getElementById("addVersionButton").addEventListener("click", openAddModal);
|
document.getElementById("versionForm").addEventListener("submit", function (event) {
|
event.preventDefault();
|
if (state.mode === "view") {
|
closeModal();
|
return;
|
}
|
saveVersion();
|
});
|
|
versionNoInput.addEventListener("input", function () {
|
versionNoInput.value = versionNoInput.value.replace(/[^\d.]/g, "").replace(/\.{2,}/g, ".");
|
});
|
|
document.querySelectorAll("[data-close='version']").forEach(function (button) {
|
button.addEventListener("click", closeModal);
|
});
|
|
uploadDrop.addEventListener("click", function () {
|
if (state.mode !== "view") {
|
packageFile.click();
|
}
|
});
|
|
packageFile.addEventListener("change", function () {
|
var file = packageFile.files && packageFile.files[0];
|
setPackageFile(file);
|
});
|
|
uploadDrop.addEventListener("dragover", function (event) {
|
if (state.mode === "view") {
|
return;
|
}
|
event.preventDefault();
|
uploadDrop.classList.add("drag-over");
|
});
|
|
uploadDrop.addEventListener("dragleave", function () {
|
uploadDrop.classList.remove("drag-over");
|
});
|
|
uploadDrop.addEventListener("drop", function (event) {
|
if (state.mode === "view") {
|
return;
|
}
|
event.preventDefault();
|
uploadDrop.classList.remove("drag-over");
|
setPackageFile(event.dataTransfer.files && event.dataTransfer.files[0]);
|
});
|
}
|
|
function loadProfileName() {
|
apiRequest("/system/user/profile", { method: "GET" }).then(function (result) {
|
applyUserInfo(result.data || {});
|
}).catch(function () {
|
applyUserInfo(readLoginUser());
|
});
|
}
|
|
function loadVersions() {
|
apiRequest("/system/version/update/page", {
|
method: "POST",
|
body: JSON.stringify({
|
pageNum: state.pageNum,
|
pageSize: state.pageSize
|
})
|
}).then(function (result) {
|
var data = result.data || {};
|
state.rows = data.records || data.list || [];
|
renderTable();
|
}).catch(function (error) {
|
state.rows = [];
|
renderTable();
|
AppMessage.error("操作失败:" + (error.message || "版本更新列表加载失败"));
|
});
|
}
|
|
function renderTable() {
|
var body = document.getElementById("versionTableBody");
|
var emptyState = document.getElementById("emptyState");
|
emptyState.hidden = state.rows.length > 0;
|
body.innerHTML = state.rows.map(function (item, index) {
|
var rowIndex = (state.pageNum - 1) * state.pageSize + index + 1;
|
return [
|
'<tr>',
|
' <td>' + rowIndex + '</td>',
|
' <td>' + escapeHtml(item.versionNo || "-") + '</td>',
|
' <td class="version-content-cell">',
|
' <span class="version-content-text">' + escapeHtml(item.updateContent || "-") + '</span>',
|
' </td>',
|
' <td>' + escapeHtml(item.createTime || "-") + '</td>',
|
' <td><div class="table-actions">',
|
' <button type="button" data-action="view" data-id="' + item.updateId + '">查看</button>',
|
' <button type="button" data-action="delete" data-id="' + item.updateId + '">删除</button>',
|
' </div></td>',
|
'</tr>'
|
].join("");
|
}).join("");
|
|
body.querySelectorAll("[data-action]").forEach(function (button) {
|
button.addEventListener("click", function () {
|
var action = button.getAttribute("data-action");
|
var updateId = Number(button.getAttribute("data-id"));
|
if (action === "view") {
|
viewVersion(updateId);
|
} else if (action === "delete") {
|
confirmDelete(updateId);
|
}
|
});
|
});
|
}
|
|
function openAddModal() {
|
state.mode = "add";
|
state.packageFile = null;
|
document.getElementById("versionModalTitle").textContent = "新建";
|
document.getElementById("versionForm").classList.remove("readonly");
|
document.getElementById("versionForm").reset();
|
document.getElementById("forceUpdate").checked = true;
|
document.getElementById("saveVersionButton").hidden = false;
|
setFormReadonly(false);
|
updatePackageTip();
|
openModal();
|
}
|
|
function viewVersion(updateId) {
|
apiRequest("/system/version/update/detail", {
|
method: "POST",
|
body: JSON.stringify({ updateId: updateId })
|
}).then(function (result) {
|
var data = result.data || {};
|
state.mode = "view";
|
state.packageFile = null;
|
document.getElementById("versionModalTitle").textContent = "查看";
|
document.getElementById("versionForm").classList.add("readonly");
|
document.getElementById("versionNo").value = removeVersionPrefix(data.versionNo || "");
|
document.getElementById("updateContent").value = data.updateContent || "";
|
document.getElementById("forceUpdate").checked = !!data.forceUpdate;
|
document.getElementById("saveVersionButton").hidden = true;
|
setFormReadonly(true);
|
updatePackageTip(data.packageName || data.packageUrl || "未上传安装包");
|
openModal();
|
}).catch(function (error) {
|
AppMessage.error("操作失败:" + (error.message || "版本详情加载失败"));
|
});
|
}
|
|
function confirmDelete(updateId) {
|
AppConfirm.open({
|
title: "是否确认删除数据?",
|
message: "删除操作不可撤回,请谨慎操作",
|
onConfirm: function () {
|
return apiRequest("/system/version/update/delete", {
|
method: "POST",
|
body: JSON.stringify({ updateIds: [updateId] })
|
}).then(function () {
|
AppMessage.success("操作成功");
|
loadVersions();
|
});
|
}
|
});
|
}
|
|
function saveVersion() {
|
var saveButton = document.getElementById("saveVersionButton");
|
var versionNumber = document.getElementById("versionNo").value.trim();
|
var updateContent = document.getElementById("updateContent").value.trim();
|
|
if (!versionNumber) {
|
AppMessage.error("操作失败:请输入版本号");
|
return;
|
}
|
if (!/^\d+(\.\d+)*$/.test(versionNumber)) {
|
AppMessage.error("操作失败:版本号格式不正确,请输入数字或使用小数点分隔");
|
return;
|
}
|
if (!updateContent) {
|
AppMessage.error("操作失败:请输入更新内容");
|
return;
|
}
|
if (!state.packageFile) {
|
AppMessage.error("操作失败:请上传安装包");
|
return;
|
}
|
if (isPackageTooLarge(state.packageFile)) {
|
AppMessage.error("操作失败:安装包大小必须小于200M");
|
return;
|
}
|
|
saveButton.disabled = true;
|
uploadPackage().then(function (fileInfo) {
|
return apiRequest("/system/version/update/add", {
|
method: "POST",
|
body: JSON.stringify({
|
versionNo: normalizeVersionNo(versionNumber),
|
updateContent: updateContent,
|
packageUrl: fileInfo.url || fileInfo.fileName || "",
|
packageName: fileInfo.originalFilename || fileInfo.newFileName || state.packageFile.name,
|
forceUpdate: document.getElementById("forceUpdate").checked
|
})
|
});
|
}).then(function () {
|
closeModal();
|
AppMessage.success("操作成功");
|
loadVersions();
|
}).catch(function (error) {
|
AppMessage.error("操作失败:" + (error.message || "版本更新保存失败"));
|
}).then(function () {
|
saveButton.disabled = false;
|
});
|
}
|
|
function uploadPackage() {
|
if (isPackageTooLarge(state.packageFile)) {
|
return Promise.reject(new Error("安装包大小必须小于200M"));
|
}
|
var formData = new FormData();
|
formData.append("file", state.packageFile);
|
return apiRequest("/system/version/update/upload", {
|
method: "POST",
|
body: formData,
|
skipJson: true
|
});
|
}
|
|
function setPackageFile(file) {
|
if (!file) {
|
state.packageFile = null;
|
updatePackageTip();
|
return;
|
}
|
if (isPackageTooLarge(file)) {
|
state.packageFile = null;
|
document.getElementById("packageFile").value = "";
|
updatePackageTip();
|
AppMessage.error("操作失败:安装包大小必须小于200M");
|
return;
|
}
|
state.packageFile = file;
|
updatePackageTip(file.name);
|
}
|
|
function isPackageTooLarge(file) {
|
return !!file && file.size >= MAX_PACKAGE_SIZE;
|
}
|
|
function updatePackageTip(fileName) {
|
var uploadDrop = document.getElementById("uploadDrop");
|
var text = fileName || "请上传安装包文件,大小须小于200M";
|
document.getElementById("packageTip").textContent = text;
|
uploadDrop.classList.toggle("has-file", !!fileName);
|
uploadDrop.querySelector("span").innerHTML = fileName
|
? '<strong>' + escapeHtml(fileName) + '</strong>'
|
: '<strong>点击上传</strong><i>/</i>拖拽到此区域';
|
}
|
|
function setFormReadonly(readonly) {
|
document.getElementById("versionNo").disabled = readonly;
|
document.getElementById("updateContent").disabled = readonly;
|
document.getElementById("forceUpdate").disabled = readonly;
|
}
|
|
function openModal() {
|
document.getElementById("versionModal").hidden = false;
|
}
|
|
function closeModal() {
|
document.getElementById("versionModal").hidden = true;
|
}
|
|
function apiRequest(path, options) {
|
options = options || {};
|
var headers = options.headers || {};
|
var token = getToken();
|
if (token) {
|
headers.Authorization = "Bearer " + token;
|
}
|
if (!options.skipJson) {
|
headers["Content-Type"] = "application/json";
|
}
|
|
return fetch(getApiBaseUrl() + normalizePath(path), {
|
method: options.method || "GET",
|
headers: headers,
|
body: options.body
|
}).then(function (response) {
|
return response.json().catch(function () {
|
return { code: response.ok ? 200 : response.status, msg: response.statusText };
|
});
|
}).then(function (result) {
|
if (result.code === 401) {
|
clearLoginState();
|
redirectToLogin();
|
throw new Error(result.msg || "登录状态已过期");
|
}
|
if (result.code !== 200) {
|
throw new Error(result.msg || "请求失败");
|
}
|
return result;
|
});
|
}
|
|
function logout() {
|
apiRequest("/logout", {
|
method: "POST",
|
skipJson: true
|
}).catch(function () {
|
return null;
|
}).then(function () {
|
clearLoginState();
|
redirectToLogin();
|
});
|
}
|
|
function applyUserInfo(user) {
|
user = user || {};
|
var displayName = user.nickName || user.userName || user.username || "用户";
|
document.getElementById("userDisplayName").textContent = displayName;
|
document.querySelector(".user-avatar").textContent = displayName.substring(0, 1);
|
}
|
|
function readLoginUser() {
|
try {
|
var loginResult = JSON.parse(localStorage.getItem("visitor_admin_login_result") || "{}");
|
return (loginResult.userInfo && loginResult.userInfo.user) || loginResult.user || {};
|
} catch (error) {
|
return {};
|
}
|
}
|
|
function normalizeVersionNo(value) {
|
var text = String(value || "").trim();
|
if (!text) {
|
return "";
|
}
|
return /^v/i.test(text) ? text : "v" + text;
|
}
|
|
function removeVersionPrefix(value) {
|
return String(value || "").replace(/^v/i, "");
|
}
|
|
function shortDate(value) {
|
return value ? String(value).slice(0, 10).replace(/-/g, ".") : "-";
|
}
|
|
function getApiBaseUrl() {
|
return trimRightSlash(localStorage.getItem("visitor_admin_api_base") || config.apiBaseUrl || window.location.origin);
|
}
|
|
function getToken() {
|
return localStorage.getItem("visitor_admin_token") || "";
|
}
|
|
function clearLoginState() {
|
localStorage.removeItem("visitor_admin_token");
|
localStorage.removeItem("visitor_admin_login_result");
|
localStorage.removeItem("visitor_admin_api_base");
|
}
|
|
function redirectToLogin() {
|
window.location.href = "../index.html";
|
}
|
|
function normalizePath(path) {
|
return "/" + String(path || "").replace(/^\/+/, "");
|
}
|
|
function trimRightSlash(value) {
|
return String(value || "").replace(/\/+$/, "");
|
}
|
|
function escapeHtml(value) {
|
return String(value == null ? "" : value).replace(/[&<>"']/g, function (char) {
|
return {
|
"&": "&",
|
"<": "<",
|
">": ">",
|
'"': """,
|
"'": "'"
|
}[char];
|
});
|
}
|
})();
|