(function () {
var config = Object.assign({
apiBaseUrl: "http://localhost:9098"
}, window.VISITOR_LOGIN_CONFIG || {});
var state = {
tree: [],
flat: [],
selectedDeptId: null,
mode: "add",
currentDeptId: null
};
init();
function init() {
if (!getToken()) {
redirectToLogin();
return;
}
bindLayoutActions();
bindPageActions();
applyUserInfo(readLoginUser());
loadProfileName();
loadDepartmentTree();
}
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("addRootDeptButton").addEventListener("click", function () {
openDepartmentModal("add", { parentId: 0, root: true, status: "0" });
});
document.getElementById("deptSearchButton").addEventListener("click", loadDepartmentTree);
document.getElementById("deptSearchInput").addEventListener("keydown", function (event) {
if (event.key === "Enter") {
loadDepartmentTree();
}
});
document.getElementById("departmentForm").addEventListener("submit", function (event) {
event.preventDefault();
saveDepartment();
});
document.querySelectorAll("[data-close='department']").forEach(function (button) {
button.addEventListener("click", closeDepartmentModal);
});
}
function loadProfileName() {
apiRequest("/system/user/profile", { method: "GET" }).then(function (result) {
applyUserInfo(result.data || {});
}).catch(function () {
applyUserInfo(readLoginUser());
});
}
function loadDepartmentTree() {
var deptName = document.getElementById("deptSearchInput").value.trim();
var path = "/system/dept/list" + (deptName ? "?deptName=" + encodeURIComponent(deptName) : "");
apiRequest(path, { method: "GET" }).then(function (result) {
var list = Array.isArray(result.data) ? result.data : [];
state.tree = normalizeDepartmentTree(list);
state.flat = flattenDepartments(state.tree);
renderTree();
}).catch(function (error) {
state.tree = [];
state.flat = [];
renderTree();
AppMessage.error("操作失败:" + (error.message || "部门列表加载失败"));
});
}
function renderTree() {
var root = document.getElementById("departmentTree");
var empty = document.getElementById("departmentEmpty");
empty.hidden = state.tree.length > 0;
root.innerHTML = state.tree.map(function (department) {
return renderNode(department, 1);
}).join("");
root.querySelectorAll("[data-action]").forEach(function (button) {
button.addEventListener("click", function (event) {
event.stopPropagation();
var action = button.getAttribute("data-action");
var deptId = Number(button.getAttribute("data-id"));
var department = findDepartment(deptId);
if (action === "addChild") {
if (department && department.level >= 5) {
AppMessage.error("操作失败:部门最多支持5级");
return;
}
openDepartmentModal("add", { parentId: deptId, status: "0" });
} else if (action === "edit") {
editDepartment(deptId);
} else if (action === "delete") {
confirmDelete(deptId);
}
});
});
root.querySelectorAll(".department-node-content").forEach(function (row) {
row.addEventListener("click", function () {
var node = row.closest(".department-node");
state.selectedDeptId = Number(node.getAttribute("data-id"));
renderTree();
});
});
root.querySelectorAll(".department-toggle").forEach(function (button) {
button.addEventListener("click", function (event) {
event.stopPropagation();
button.closest(".department-node").classList.toggle("collapsed");
});
});
}
function renderNode(department, level) {
var children = Array.isArray(department.children) ? department.children : [];
var hasChildren = children.length > 0;
var active = state.selectedDeptId === department.deptId;
var disabled = isDepartmentDisabled(department);
var childHtml = hasChildren ? '
' + children.map(function (child) {
return renderNode(child, level + 1);
}).join("") + '
' : "";
var rowClass = "department-node-content" + (active ? " active" : "") + (disabled ? " disabled" : "");
var addButton = level >= 5
? ''
: '';
var statusBadge = disabled ? '禁' : '';
var actions = disabled ? [
' ' + statusBadge,
' ',
' ',
' '
] : [
' ' + addButton,
' ',
' ',
' ' + statusBadge
];
if (disabled) {
statusBadge = '禁';
actions = [
' ' + statusBadge,
' ' + addButton,
' ',
' '
];
}
statusBadge = disabled ? '禁' : '';
addButton = level >= 5
? ''
: '';
actions = [
' ' + statusBadge,
' ' + addButton,
' ',
' '
];
return [
'',
'
',
' ',
' ',
' ' + escapeHtml(department.deptName || "-") + '',
actions.join(""),
'
',
childHtml,
'
'
].join("");
}
function openDepartmentModal(mode, department) {
department = department || {};
state.mode = mode;
state.currentDeptId = mode === "edit" ? department.deptId : null;
document.getElementById("departmentModalTitle").textContent = mode === "edit" ? "编辑部门" : "新增部门";
document.getElementById("deptName").value = department.deptName || "";
document.getElementById("deptStatus").checked = !isDepartmentDisabled(department);
renderParentOptions(Number(department.parentId || 0), state.currentDeptId);
var parentRow = document.getElementById("parentDeptRow");
parentRow.hidden = mode === "add" && department.root === true;
document.getElementById("departmentModal").hidden = false;
}
function closeDepartmentModal() {
document.getElementById("departmentModal").hidden = true;
}
function editDepartment(deptId) {
apiRequest("/system/dept/" + deptId, { method: "GET" }).then(function (result) {
openDepartmentModal("edit", result.data || {});
}).catch(function (error) {
AppMessage.error("操作失败:" + (error.message || "部门详情加载失败"));
});
}
function saveDepartment() {
var saveButton = document.getElementById("saveDepartmentButton");
var deptName = document.getElementById("deptName").value.trim();
if (!deptName) {
AppMessage.error("操作失败:请输入部门名称");
return;
}
var parentRow = document.getElementById("parentDeptRow");
var payload = {
parentId: parentRow.hidden ? 0 : Number(document.getElementById("parentDeptId").value || 0),
deptName: deptName,
orderNum: 0,
status: document.getElementById("deptStatus").checked ? "0" : "1"
};
var method = "POST";
var path = "/system/dept";
if (state.mode === "edit") {
payload.deptId = state.currentDeptId;
method = "PUT";
}
saveButton.disabled = true;
apiRequest(path, {
method: method,
body: JSON.stringify(payload)
}).then(function () {
closeDepartmentModal();
AppMessage.success("操作成功");
loadDepartmentTree();
}).catch(function (error) {
AppMessage.error("操作失败:" + (error.message || "部门保存失败"));
}).then(function () {
saveButton.disabled = false;
});
}
function confirmDelete(deptId) {
AppConfirm.open({
title: "是否确认删除数据?",
message: "删除操作不可撤回,请谨慎操作",
onConfirm: function () {
return apiRequest("/system/dept/" + deptId, {
method: "DELETE",
skipJson: true
}).then(function () {
AppMessage.success("操作成功");
loadDepartmentTree();
});
}
});
}
function renderParentOptions(selectedParentId, currentDeptId) {
var select = document.getElementById("parentDeptId");
var options = [''];
state.flat.forEach(function (department) {
if (department.deptId === currentDeptId || isDescendant(department, currentDeptId)) {
return;
}
options.push('');
});
select.innerHTML = options.join("");
select.value = String(selectedParentId || 0);
}
function normalizeDepartmentTree(list) {
var cloned = list.map(function (item) {
var department = Object.assign({}, item);
department.deptId = Number(department.deptId);
department.parentId = Number(department.parentId || 0);
department.children = Array.isArray(item.children) ? normalizeDepartmentTree(item.children) : [];
return department;
});
var hasNestedData = cloned.some(function (item) {
return item.children.length > 0;
});
if (hasNestedData) {
return cloned;
}
var map = {};
var roots = [];
cloned.forEach(function (item) {
item.children = [];
map[item.deptId] = item;
});
cloned.forEach(function (item) {
var parent = map[item.parentId];
if (parent) {
parent.children.push(item);
} else {
roots.push(item);
}
});
return roots;
}
function flattenDepartments(tree) {
var result = [];
tree.forEach(function (department) {
walkDepartment(department, 1, result);
});
return result;
}
function walkDepartment(department, level, result) {
department.level = level;
result.push(department);
(department.children || []).forEach(function (child) {
child.parentId = department.deptId;
walkDepartment(child, level + 1, result);
});
}
function isDescendant(department, parentId) {
if (!parentId || !department) {
return false;
}
var current = department;
while (current && current.parentId) {
if (current.parentId === parentId) {
return true;
}
current = findDepartment(current.parentId);
}
return false;
}
function findDepartment(deptId) {
for (var index = 0; index < state.flat.length; index++) {
if (Number(state.flat[index].deptId) === Number(deptId)) {
return state.flat[index];
}
}
return null;
}
function isDepartmentDisabled(department) {
return department && (department.status === "1" || department.status === 1 || department.enabled === false || department.disabled === true);
}
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 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];
});
}
})();