(function () { var config = Object.assign({ apiBaseUrl: "http://localhost:9098" }, window.VISITOR_LOGIN_CONFIG || {}); var state = { areaTree: [], flatAreas: [], selectedAreaId: null, modalAreaId: null, cascaderPath: [], rows: [], total: 0, pageNum: 1, pageSize: 10, mode: "add", currentDoorId: null }; init(); function init() { if (!getToken()) { redirectToLogin(); return; } bindLayoutActions(); bindPageActions(); applyUserInfo(readLoginUser()); loadProfileName(); loadAreaTree(); loadDoorDevices(); } 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"); closeMoreMenus(); closeAreaCascader(); }); fullscreenButton.addEventListener("click", function () { if (!document.fullscreenElement && document.documentElement.requestFullscreen) { document.documentElement.requestFullscreen(); } else if (document.exitFullscreen) { document.exitFullscreen(); } }); } function bindPageActions() { document.getElementById("areaSearchButton").addEventListener("click", loadAreaTree); document.getElementById("areaSearchInput").addEventListener("keydown", function (event) { if (event.key === "Enter") { loadAreaTree(); } }); document.getElementById("queryForm").addEventListener("submit", function (event) { event.preventDefault(); state.pageNum = 1; loadDoorDevices(); }); document.getElementById("resetButton").addEventListener("click", function () { document.getElementById("queryDeviceName").value = ""; document.getElementById("queryDeviceId").value = ""; document.getElementById("queryEnabled").value = ""; state.pageNum = 1; loadDoorDevices(); }); document.getElementById("addDoorButton").addEventListener("click", function () { openDoorModal("add", {}); }); document.getElementById("doorForm").addEventListener("submit", function (event) { event.preventDefault(); saveDoorDevice(); }); document.querySelectorAll("[data-close='door']").forEach(function (button) { button.addEventListener("click", closeDoorModal); }); document.getElementById("areaCascaderTrigger").addEventListener("click", function (event) { event.stopPropagation(); toggleAreaCascader(); }); document.getElementById("areaCascaderPanel").addEventListener("click", function (event) { event.stopPropagation(); }); } 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.areaTree = Array.isArray(result.data) ? result.data : []; state.flatAreas = flattenAreas(state.areaTree); renderAreaTree(); renderAreaCascaderPanel(); }).catch(function (error) { state.areaTree = []; state.flatAreas = []; state.selectedAreaId = null; state.modalAreaId = null; state.cascaderPath = []; renderAreaTree(); renderAreaCascaderPanel(); updateCascaderText(); AppMessage.error("操作失败:" + (error.message || "区域加载失败")); }); } function loadDoorDevices() { var enabledValue = document.getElementById("queryEnabled").value; var payload = { pageNum: state.pageNum, pageSize: state.pageSize, areaId: state.selectedAreaId, deviceName: document.getElementById("queryDeviceName").value.trim(), deviceId: document.getElementById("queryDeviceId").value.trim() }; if (enabledValue) { payload.enabled = enabledValue === "true"; } apiRequest("/system/door/device/page", { method: "POST", body: JSON.stringify(payload) }).then(function (result) { var data = result.data || {}; state.rows = data.records || data.list || []; state.total = Number(data.total || state.rows.length || 0); state.pageNum = Number(data.current || data.pageNum || state.pageNum); state.pageSize = Number(data.size || data.pageSize || state.pageSize); renderTable(); renderPagination(); }).catch(function (error) { state.rows = []; state.total = 0; renderTable(); renderPagination(); AppMessage.error("操作失败:" + (error.message || "门禁设备列表加载失败")); }); } function renderAreaTree() { var root = document.getElementById("areaTree"); root.innerHTML = state.areaTree.map(function (area) { return renderAreaNode(area, 1); }).join(""); root.querySelectorAll(".door-area-row").forEach(function (row) { row.addEventListener("click", function () { state.selectedAreaId = Number(row.getAttribute("data-id")); state.pageNum = 1; renderAreaTree(); loadDoorDevices(); }); }); root.querySelectorAll(".door-area-toggle").forEach(function (toggle) { toggle.addEventListener("click", function (event) { event.stopPropagation(); toggle.closest(".door-area-node").classList.toggle("collapsed"); }); }); } function renderAreaNode(area, level) { var children = Array.isArray(area.children) ? area.children : []; var active = state.selectedAreaId === area.areaId; var childHtml = children.length ? '
' + children.map(function (child) { return renderAreaNode(child, level + 1); }).join("") + '
' : ""; return [ '
', '
', ' ', ' ', ' ' + escapeHtml(area.areaName || "-") + '', '
', childHtml, '
' ].join(""); } function renderTable() { var body = document.getElementById("doorTableBody"); var empty = document.getElementById("doorEmpty"); empty.hidden = state.rows.length > 0; body.innerHTML = state.rows.map(function (item, index) { var rowIndex = (state.pageNum - 1) * state.pageSize + index + 1; return [ '', ' ' + rowIndex + '', ' ' + escapeHtml(item.deviceName || "-") + '', ' ' + escapeHtml(item.brand || "-") + '', ' ' + escapeHtml(item.deviceId || "-") + '', ' ' + escapeHtml(item.areaName || "-") + '', ' ' + renderEnabledStatus(item) + '', ' ' + renderOnlineStatus(item.onlineStatus) + '', ' ' + escapeHtml(item.remark || "-") + '', '
', ' ', ' ', renderMoreMenu(item), '
', '' ].join(""); }).join(""); body.querySelectorAll("[data-action]").forEach(function (button) { button.addEventListener("click", function (event) { event.stopPropagation(); var action = button.getAttribute("data-action"); var doorId = Number(button.getAttribute("data-id")); if (action === "edit") { editDoorDevice(doorId); } else if (action === "more") { toggleMoreMenu(button); } else if (action === "logout") { confirmDoorAction(doorId, "是否确认注销设备?", "注销后设备在线状态将变为未激活", logoutDoorDevice); } else if (action === "disable") { changeEnabled(doorId, false); } else if (action === "enable") { changeEnabled(doorId, true); } else if (action === "delete") { confirmDoorAction(doorId, "是否确认删除数据?", "删除操作不可撤回,请谨慎操作", deleteDoorDevice); } }); }); } function renderMoreMenu(item) { var statusAction = isEnabled(item) ? '' : ''; return [ '' ].join(""); } function renderEnabledStatus(item) { var enabled = isEnabled(item); var className = enabled ? "enabled" : "disabled"; var label = enabled ? "启用" : "禁用"; return '' + label + ''; } function renderOnlineStatus(status) { var className = "unactivated"; if (status === "ONLINE") { className = "online"; } else if (status === "OFFLINE") { className = "offline"; } return '' + escapeHtml(formatOnlineStatus(status)) + ''; } function renderPagination() { var root = document.getElementById("pagination"); var totalPage = Math.max(1, Math.ceil(state.total / state.pageSize)); var pages = []; for (var page = 1; page <= Math.min(totalPage, 5); page++) { pages.push(''); } if (totalPage > 5) { pages.push('...'); } root.innerHTML = [ '共 ' + state.total + ' 条', '每页 条', pages.join(""), '前往 页' ].join(""); document.getElementById("pageSizeSelect").value = String(state.pageSize); root.querySelectorAll("[data-page]").forEach(function (button) { button.addEventListener("click", function () { state.pageNum = Number(button.getAttribute("data-page")); loadDoorDevices(); }); }); document.getElementById("pageSizeSelect").addEventListener("change", function (event) { state.pageSize = Number(event.target.value); state.pageNum = 1; loadDoorDevices(); }); document.getElementById("jumpPageInput").addEventListener("keydown", function (event) { if (event.key === "Enter") { var page = Math.min(totalPage, Math.max(1, Number(event.target.value) || 1)); state.pageNum = page; loadDoorDevices(); } }); } function openDoorModal(mode, data) { state.mode = mode; state.currentDoorId = mode === "edit" ? getDoorId(data) : null; document.getElementById("doorModalTitle").textContent = mode === "edit" ? "编辑门禁" : "新增门禁"; document.getElementById("doorDeviceName").value = data.deviceName || ""; document.getElementById("doorBrand").value = data.brand || ""; document.getElementById("doorDeviceId").value = data.deviceId || ""; document.getElementById("doorRemark").value = data.remark || ""; document.getElementById("doorEnabled").checked = typeof data.enabled === "boolean" ? data.enabled : true; setCascaderValue(data.areaId || state.selectedAreaId || ""); document.getElementById("doorModal").hidden = false; } function closeDoorModal() { document.getElementById("doorModal").hidden = true; closeAreaCascader(); } function editDoorDevice(doorId) { apiRequest("/system/door/device/detail", { method: "POST", body: JSON.stringify({ doorId: doorId }) }).then(function (result) { openDoorModal("edit", result.data || {}); }).catch(function (error) { AppMessage.error("操作失败:" + (error.message || "设备详情加载失败")); }); } function saveDoorDevice() { var saveButton = document.getElementById("saveDoorButton"); var payload = { areaId: Number(state.modalAreaId || 0), deviceName: document.getElementById("doorDeviceName").value.trim(), brand: document.getElementById("doorBrand").value.trim(), deviceId: document.getElementById("doorDeviceId").value.trim(), remark: document.getElementById("doorRemark").value.trim(), enabled: document.getElementById("doorEnabled").checked }; if (!payload.deviceName) { AppMessage.error("操作失败:请输入设备名称"); return; } if (!payload.brand) { AppMessage.error("操作失败:请输入品牌"); return; } if (!payload.deviceId) { AppMessage.error("操作失败:请输入设备ID"); return; } if (!payload.areaId) { AppMessage.error("操作失败:请选择区域"); return; } var url = "/system/door/device/add"; if (state.mode === "edit") { payload.doorId = state.currentDoorId; url = "/system/door/device/edit"; } saveButton.disabled = true; apiRequest(url, { method: "POST", body: JSON.stringify(payload) }).then(function () { closeDoorModal(); AppMessage.success("操作成功"); loadDoorDevices(); }).catch(function (error) { AppMessage.error("操作失败:" + (error.message || "设备保存失败")); }).then(function () { saveButton.disabled = false; }); } function changeEnabled(doorId, enabled) { apiRequest("/system/door/device/changeStatus", { method: "POST", body: JSON.stringify({ doorIds: [doorId], enabled: enabled }) }).then(function () { AppMessage.success("操作成功"); loadDoorDevices(); }).catch(function (error) { AppMessage.error("操作失败:" + (error.message || "状态更新失败")); }); } function logoutDoorDevice(doorId) { return apiRequest("/system/door/device/logout", { method: "POST", body: JSON.stringify({ doorIds: [doorId] }) }).then(function () { AppMessage.success("操作成功"); loadDoorDevices(); }); } function deleteDoorDevice(doorId) { return apiRequest("/system/door/device/delete", { method: "POST", body: JSON.stringify({ doorIds: [doorId] }) }).then(function () { AppMessage.success("操作成功"); loadDoorDevices(); }); } function confirmDoorAction(doorId, title, message, handler) { AppConfirm.open({ title: title, message: message, onConfirm: function () { return handler(doorId); } }); } function toggleAreaCascader() { var panel = document.getElementById("areaCascaderPanel"); if (panel.hidden) { renderAreaCascaderPanel(); panel.hidden = false; return; } panel.hidden = true; } function closeAreaCascader() { document.getElementById("areaCascaderPanel").hidden = true; } function setCascaderValue(areaId) { state.modalAreaId = areaId ? Number(areaId) : null; state.cascaderPath = state.modalAreaId ? findAreaPath(state.modalAreaId) : []; updateCascaderText(); renderAreaCascaderPanel(); } function renderAreaCascaderPanel() { var panel = document.getElementById("areaCascaderPanel"); var columns = buildCascaderColumns(); panel.innerHTML = columns.map(function (column, columnIndex) { return '
' + column.items.map(function (area) { var active = column.selectedId === area.areaId; var hasChildren = Array.isArray(area.children) && area.children.length > 0; return [ '' ].join(""); }).join("") + '
'; }).join(""); panel.querySelectorAll(".area-cascader-option").forEach(function (button) { button.addEventListener("click", function () { var level = Number(button.getAttribute("data-level")); var areaId = Number(button.getAttribute("data-id")); var area = findArea(areaId); if (!area) { return; } state.cascaderPath = state.cascaderPath.slice(0, level); state.cascaderPath[level] = area; state.modalAreaId = area.areaId; updateCascaderText(); renderAreaCascaderPanel(); if (!area.children || !area.children.length) { closeAreaCascader(); } }); }); } function buildCascaderColumns() { var columns = [{ items: state.areaTree, selectedId: state.cascaderPath[0] && state.cascaderPath[0].areaId }]; state.cascaderPath.forEach(function (area, index) { var children = Array.isArray(area.children) ? area.children : []; if (children.length) { columns.push({ items: children, selectedId: state.cascaderPath[index + 1] && state.cascaderPath[index + 1].areaId }); } }); return columns; } function updateCascaderText() { var text = state.cascaderPath.length ? state.cascaderPath.map(function (area) { return area.areaName; }).join(" / ") : "下拉菜单"; document.getElementById("areaCascaderText").textContent = text; } function flattenAreas(tree) { var result = []; tree.forEach(function (area) { walkArea(area, 1, result); }); return result; } function walkArea(area, level, result) { area.level = level; result.push(area); (area.children || []).forEach(function (child) { child.parentId = area.areaId; walkArea(child, level + 1, result); }); } function findArea(areaId) { for (var index = 0; index < state.flatAreas.length; index++) { if (state.flatAreas[index].areaId === areaId) { return state.flatAreas[index]; } } return null; } function findAreaPath(areaId) { var result = []; walkPath(state.areaTree, Number(areaId), result); return result; } function walkPath(list, areaId, path) { for (var index = 0; index < list.length; index++) { var area = list[index]; path.push(area); if (area.areaId === areaId) { return true; } if (walkPath(area.children || [], areaId, path)) { return true; } path.pop(); } return false; } function toggleMoreMenu(button) { var menu = button.parentNode.querySelector(".door-more-menu"); var hidden = menu.hidden; closeMoreMenus(); menu.hidden = !hidden; } function closeMoreMenus() { document.querySelectorAll(".door-more-menu").forEach(function (menu) { menu.hidden = true; }); } function getDoorId(item) { return Number(item.doorId || item.deviceId || item.id || 0); } function isEnabled(item) { return item.enabled === true || item.enabled === "true" || item.enabled === 1 || item.enabled === "1"; } function formatOnlineStatus(status) { if (status === "ONLINE") { return "在线"; } if (status === "OFFLINE") { return "离线"; } return "未激活"; } 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]; }); } })();