(function () {
|
var config = Object.assign({
|
apiBaseUrl: "http://localhost:9098"
|
}, window.VISITOR_LOGIN_CONFIG || {});
|
|
var state = {
|
tree: [],
|
flat: [],
|
selectedAreaId: null,
|
mode: "add",
|
currentAreaId: null
|
};
|
|
init();
|
|
function init() {
|
if (!getToken()) {
|
redirectToLogin();
|
return;
|
}
|
bindLayoutActions();
|
bindPageActions();
|
applyUserInfo(readLoginUser());
|
loadProfileName();
|
loadAreaTree();
|
}
|
|
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() {
|
document.getElementById("addRootButton").addEventListener("click", function () {
|
openAreaModal("add", { parentId: 0 });
|
});
|
|
document.getElementById("areaSearchButton").addEventListener("click", function () {
|
loadAreaTree();
|
});
|
|
document.getElementById("areaSearchInput").addEventListener("keydown", function (event) {
|
if (event.key === "Enter") {
|
loadAreaTree();
|
}
|
});
|
|
document.getElementById("areaForm").addEventListener("submit", function (event) {
|
event.preventDefault();
|
saveArea();
|
});
|
|
document.querySelectorAll("[data-close='area']").forEach(function (button) {
|
button.addEventListener("click", closeAreaModal);
|
});
|
}
|
|
function loadProfileName() {
|
apiRequest("/system/user/profile", { method: "GET" }).then(function (result) {
|
applyUserInfo(result.data || {});
|
}).catch(function () {
|
applyUserInfo(readLoginUser());
|
});
|
}
|
|
function loadAreaTree() {
|
apiRequest("/system/visit/area/tree", {
|
method: "POST",
|
body: JSON.stringify({
|
areaName: document.getElementById("areaSearchInput").value.trim()
|
})
|
}).then(function (result) {
|
state.tree = Array.isArray(result.data) ? result.data : [];
|
state.flat = flattenAreas(state.tree);
|
renderTree();
|
}).catch(function (error) {
|
state.tree = [];
|
state.flat = [];
|
renderTree();
|
AppMessage.error("操作失败:" + (error.message || "区域列表加载失败"));
|
});
|
}
|
|
function renderTree() {
|
var root = document.getElementById("areaTree");
|
var empty = document.getElementById("areaEmpty");
|
empty.hidden = state.tree.length > 0;
|
root.innerHTML = state.tree.map(function (area, index) {
|
return renderNode(area, 1, index + 1);
|
}).join("");
|
|
root.querySelectorAll("[data-action]").forEach(function (button) {
|
button.addEventListener("click", function (event) {
|
event.stopPropagation();
|
var action = button.getAttribute("data-action");
|
var areaId = Number(button.getAttribute("data-id"));
|
if (action === "addChild") {
|
openAreaModal("add", { parentId: areaId });
|
} else if (action === "edit") {
|
editArea(areaId);
|
} else if (action === "delete") {
|
confirmDelete(areaId);
|
}
|
});
|
});
|
|
root.querySelectorAll(".area-node-content").forEach(function (row) {
|
row.addEventListener("click", function () {
|
var node = row.closest(".area-node");
|
state.selectedAreaId = Number(node.getAttribute("data-id"));
|
renderTree();
|
});
|
});
|
|
root.querySelectorAll(".area-toggle").forEach(function (button) {
|
button.addEventListener("click", function (event) {
|
event.stopPropagation();
|
button.closest(".area-node").classList.toggle("collapsed");
|
});
|
});
|
}
|
|
function renderNode(area, level, index) {
|
var children = Array.isArray(area.children) ? area.children : [];
|
var hasChildren = children.length > 0;
|
var active = state.selectedAreaId === area.areaId;
|
var disabled = isAreaDisabled(area);
|
var childHtml = hasChildren ? '<div class="area-children">' + children.map(function (child, childIndex) {
|
return renderNode(child, level + 1, childIndex + 1);
|
}).join("") + '</div>' : "";
|
var rowClass = "area-node-content" + (active ? " active" : "") + (disabled ? " disabled" : "");
|
var actions = disabled ? [
|
' <span class="area-disabled-tip"><span class="area-disabled-badge">禁</span><span>当前部门已禁用,禁止点击</span></span>'
|
] : [
|
' <button class="area-action add-child" type="button" data-action="addChild" data-id="' + area.areaId + '">添加下级</button>',
|
' <button class="area-action edit" type="button" data-action="edit" data-id="' + area.areaId + '">编辑</button>',
|
' <button class="area-action delete" type="button" data-action="delete" data-id="' + area.areaId + '">删除</button>',
|
' <span class="area-disabled-tip"></span>'
|
];
|
return [
|
'<div class="area-node level-' + level + (hasChildren ? "" : " no-children") + '" data-id="' + area.areaId + '">',
|
' <div class="' + rowClass + '">',
|
' <button class="area-toggle" type="button" title="展开/收起"></button>',
|
' <span class="' + (level === 1 ? "area-folder-icon" : "area-file-icon") + '"></span>',
|
' <span class="area-name">' + escapeHtml(area.areaName || "-") + '</span>',
|
actions.join(""),
|
' </div>',
|
childHtml,
|
'</div>'
|
].join("");
|
}
|
|
function isAreaDisabled(area) {
|
return area && (area.enabled === false || area.disabled === true || area.status === "1" || area.status === 1);
|
}
|
|
function openAreaModal(mode, area) {
|
state.mode = mode;
|
state.currentAreaId = mode === "edit" ? area.areaId : null;
|
document.getElementById("areaModalTitle").textContent = mode === "edit" ? "编辑区域" : "添加区域";
|
renderParentOptions(area.parentId || 0, state.currentAreaId);
|
document.getElementById("areaCode").value = area.areaCode || "";
|
document.getElementById("areaName").value = area.areaName || "";
|
document.getElementById("areaDesc").value = area.areaDesc || "";
|
document.getElementById("areaModal").hidden = false;
|
}
|
|
function closeAreaModal() {
|
document.getElementById("areaModal").hidden = true;
|
}
|
|
function editArea(areaId) {
|
apiRequest("/system/visit/area/detail", {
|
method: "POST",
|
body: JSON.stringify({ areaId: areaId })
|
}).then(function (result) {
|
openAreaModal("edit", result.data || {});
|
}).catch(function (error) {
|
AppMessage.error("操作失败:" + (error.message || "区域详情加载失败"));
|
});
|
}
|
|
function saveArea() {
|
var saveButton = document.getElementById("saveAreaButton");
|
var areaName = document.getElementById("areaName").value.trim();
|
if (!areaName) {
|
AppMessage.error("操作失败:请输入区域名称");
|
return;
|
}
|
var payload = {
|
parentId: Number(document.getElementById("parentId").value || 0),
|
areaCode: document.getElementById("areaCode").value.trim(),
|
areaName: areaName,
|
areaDesc: document.getElementById("areaDesc").value.trim(),
|
orderNum: 0
|
};
|
var url = "/system/visit/area/add";
|
if (state.mode === "edit") {
|
payload.areaId = state.currentAreaId;
|
url = "/system/visit/area/edit";
|
}
|
|
saveButton.disabled = true;
|
apiRequest(url, {
|
method: "POST",
|
body: JSON.stringify(payload)
|
}).then(function () {
|
closeAreaModal();
|
AppMessage.success("操作成功");
|
loadAreaTree();
|
}).catch(function (error) {
|
AppMessage.error("操作失败:" + (error.message || "区域保存失败"));
|
}).then(function () {
|
saveButton.disabled = false;
|
});
|
}
|
|
function confirmDelete(areaId) {
|
AppConfirm.open({
|
title: "是否确认删除数据?",
|
message: "删除操作不可撤回,请谨慎操作",
|
onConfirm: function () {
|
return apiRequest("/system/visit/area/delete", {
|
method: "POST",
|
body: JSON.stringify({ areaIds: [areaId] })
|
}).then(function () {
|
AppMessage.success("操作成功");
|
loadAreaTree();
|
});
|
}
|
});
|
}
|
|
function renderParentOptions(selectedParentId, currentAreaId) {
|
var select = document.getElementById("parentId");
|
var options = ['<option value="0">不选择表示为一级区域</option>'];
|
state.flat.forEach(function (area) {
|
if (area.areaId === currentAreaId || isDescendant(area, currentAreaId)) {
|
return;
|
}
|
options.push('<option value="' + area.areaId + '">' + escapeHtml(indentText(area.level) + area.areaName) + '</option>');
|
});
|
select.innerHTML = options.join("");
|
select.value = String(selectedParentId || 0);
|
}
|
|
function flattenAreas(tree) {
|
var result = [];
|
tree.forEach(function (area) {
|
walk(area, 1, result);
|
});
|
return result;
|
}
|
|
function walk(area, level, result) {
|
area.level = level;
|
result.push(area);
|
(area.children || []).forEach(function (child) {
|
walk(child, level + 1, result);
|
});
|
}
|
|
function isDescendant(area, parentId) {
|
if (!parentId || !area) {
|
return false;
|
}
|
var current = area;
|
while (current && current.parentId) {
|
if (current.parentId === parentId) {
|
return true;
|
}
|
current = findArea(current.parentId);
|
}
|
return false;
|
}
|
|
function findArea(areaId) {
|
for (var index = 0; index < state.flat.length; index++) {
|
if (state.flat[index].areaId === areaId) {
|
return state.flat[index];
|
}
|
}
|
return null;
|
}
|
|
function indentText(level) {
|
return new Array(level).join(" ");
|
}
|
|
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 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];
|
});
|
}
|
})();
|