(function () { var config = Object.assign({ apiBaseUrl: "http://localhost:9098" }, window.VISITOR_LOGIN_CONFIG || {}); var state = { areaTree: [], rows: [], total: 0, pageNum: 1, pageSize: 10, areas: [], selectedAreaId: "", cascaderPath: [] }; init(); function init() { if (!getToken()) { redirectToLogin(); return; } bindLayoutActions(); bindPageActions(); applyUserInfo(readLoginUser()); loadProfileName(); loadAreas(); loadRecords(); } 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"); closeAreaCascader(); }); fullscreenButton.addEventListener("click", function () { if (!document.fullscreenElement && document.documentElement.requestFullscreen) { document.documentElement.requestFullscreen(); } else if (document.exitFullscreen) { document.exitFullscreen(); } }); } function bindPageActions() { var areaTrigger = document.getElementById("areaCascaderTrigger"); var areaPanel = document.getElementById("areaCascaderPanel"); areaTrigger.addEventListener("click", function (event) { event.stopPropagation(); toggleAreaCascader(); }); areaPanel.addEventListener("click", function (event) { event.stopPropagation(); var option = event.target.closest(".area-cascader-option"); if (!option) { return; } selectAreaOption(option); }); document.getElementById("queryForm").addEventListener("submit", function (event) { event.preventDefault(); state.pageNum = 1; loadRecords(); }); document.getElementById("resetButton").addEventListener("click", function () { document.getElementById("queryForm").reset(); setAreaValue(""); state.pageNum = 1; loadRecords(); }); document.getElementById("exportButton").addEventListener("click", function () { AppMessage.error("操作失败:导出接口后续补充"); }); } function loadProfileName() { apiRequest("/system/user/profile", { method: "GET" }).then(function (result) { applyUserInfo(result.data || {}); }).catch(function () { applyUserInfo(readLoginUser()); }); } function loadAreas() { apiRequest("/system/visit/area/tree", { method: "POST", body: JSON.stringify({}) }).then(function (result) { state.areaTree = normalizeAreaTree(Array.isArray(result.data) ? result.data : []); state.areas = flattenAreas(state.areaTree); if (state.selectedAreaId && !findArea(state.selectedAreaId)) { setAreaValue(""); } else { setAreaValue(state.selectedAreaId); } renderAreaCascaderPanel(); }).catch(function () { state.areaTree = []; state.areas = []; setAreaValue(""); renderAreaCascaderPanel(); }); } function loadRecords() { var payload = { pageNum: state.pageNum, pageSize: state.pageSize, intervieweeName: document.getElementById("queryInterviewee").value.trim(), intervieweeDept: document.getElementById("queryDept").value.trim(), areaId: numberOrNull(document.getElementById("queryArea").value), validStartTime: toServerDateTime(document.getElementById("queryStartTime").value), validEndTime: toServerDateTime(document.getElementById("queryEndTime").value), status: document.getElementById("queryStatus").value, visitorName: document.getElementById("queryVisitor").value.trim(), visitReason: document.getElementById("queryReason").value }; apiRequest("/system/visitor/record/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 renderTable() { var body = document.getElementById("recordTableBody"); var empty = document.getElementById("recordEmpty"); 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.visitorName || "-") + '', '' + escapeHtml(maskIdCard(item.idCard)) + '', '' + escapeHtml(formatRegisterType(item.registerType)) + '', '' + escapeHtml(item.intervieweeName || "-") + '', '' + escapeHtml(item.intervieweeDept || "-") + '', '' + escapeHtml(item.visitAreaNames || "-") + '', '' + escapeHtml(item.plateNo || "-") + '', '' + escapeHtml(item.visitReason || "-") + '', '' + escapeHtml(formatValidTime(item.validStartTime, item.validEndTime)) + '', '' + renderStatus(item.status) + '', '' + escapeHtml(item.registerTime || "-") + '', '
', '', '', '
', '' ].join(""); }).join(""); body.querySelectorAll("[data-action]").forEach(function (button) { button.addEventListener("click", function () { var action = button.getAttribute("data-action"); var recordId = Number(button.getAttribute("data-id")); if (action === "detail") { window.location.href = "./visitor-record-detail.html?recordId=" + recordId; } else if (action === "delete") { confirmDelete(recordId); } }); }); } function renderStatus(status) { var map = { VISITING: ["visiting", "来访中"], PENDING_CONFIRM: ["pending", "逾期滞留"], SIGNED_OUT: ["signed", "已签离"], OVERDUE_AUTO_SIGNED_OUT: ["overdue", "超期自动签离"], CANCELLED: ["cancelled", "已取消"] }; var item = map[status] || ["cancelled", status || "-"]; return '' + escapeHtml(item[1]) + ''; } 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")); loadRecords(); }); }); document.getElementById("pageSizeSelect").addEventListener("change", function (event) { state.pageSize = Number(event.target.value); state.pageNum = 1; loadRecords(); }); document.getElementById("jumpPageInput").addEventListener("keydown", function (event) { if (event.key === "Enter") { state.pageNum = Math.min(totalPage, Math.max(1, Number(event.target.value) || 1)); loadRecords(); } }); } function confirmDelete(recordId) { AppConfirm.open({ title: "是否确认删除数据?", message: "删除操作不可撤回,请谨慎操作", onConfirm: function () { return apiRequest("/system/visitor/record/delete", { method: "POST", body: JSON.stringify({ recordIds: [recordId] }) }).then(function () { AppMessage.success("操作成功"); loadRecords(); }); } }); } 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 toggleAreaCascader() { var panel = document.getElementById("areaCascaderPanel"); if (panel.hidden) { renderAreaCascaderPanel(); panel.hidden = false; document.getElementById("areaCascaderTrigger").setAttribute("aria-expanded", "true"); return; } closeAreaCascader(); } function closeAreaCascader() { var panel = document.getElementById("areaCascaderPanel"); if (!panel) { return; } panel.hidden = true; document.getElementById("areaCascaderTrigger").setAttribute("aria-expanded", "false"); } function setAreaValue(areaId) { state.selectedAreaId = areaId ? String(areaId) : ""; state.cascaderPath = state.selectedAreaId ? findAreaPath(state.selectedAreaId) : []; document.getElementById("queryArea").value = state.selectedAreaId; updateAreaCascaderText(); renderAreaCascaderPanel(); } function selectAreaOption(option) { var level = Number(option.getAttribute("data-level")); var areaId = option.getAttribute("data-id"); var area = findArea(areaId); if (!area) { return; } state.cascaderPath = state.cascaderPath.slice(0, level); state.cascaderPath[level] = area; state.selectedAreaId = String(area.areaId); document.getElementById("queryArea").value = state.selectedAreaId; updateAreaCascaderText(); renderAreaCascaderPanel(); if (!area.children || !area.children.length) { closeAreaCascader(); } } function renderAreaCascaderPanel() { var panel = document.getElementById("areaCascaderPanel"); var columns = buildAreaCascaderColumns(); if (!columns.length) { panel.innerHTML = '
暂无区域
'; return; } panel.innerHTML = columns.map(function (column, columnIndex) { return '
' + column.items.map(function (area) { var active = String(column.selectedId || "") === String(area.areaId); var hasChildren = Array.isArray(area.children) && area.children.length > 0; return [ '' ].join(""); }).join("") + '
'; }).join(""); } function buildAreaCascaderColumns() { if (!state.areaTree.length) { return []; } 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 updateAreaCascaderText() { var text = state.cascaderPath.length ? state.cascaderPath.map(function (area) { return area.areaName; }).join(" / ") : "下拉菜单"; document.getElementById("areaCascaderText").textContent = text; } function findArea(areaId) { var targetId = Number(areaId); if (!targetId) { return null; } for (var index = 0; index < state.areas.length; index++) { if (Number(state.areas[index].areaId) === targetId) { return state.areas[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 (Number(area.areaId) === areaId) { return true; } if (walkPath(area.children || [], areaId, path)) { return true; } path.pop(); } return false; } function normalizeAreaTree(list) { var result = []; (list || []).forEach(function (area) { normalizeArea(area).forEach(function (item) { result.push(item); }); }); return result; } function normalizeArea(area) { var children = normalizeAreaTree(getAreaChildren(area)); var areaId = getAreaId(area); var areaName = getAreaName(area); if (!areaId || !areaName) { return children; } return [{ areaId: Number(areaId), areaName: areaName, children: children }]; } function getAreaId(area) { var value = area && area.areaId != null ? area.areaId : area && area.id != null ? area.id : area && area.value; var number = Number(value); return number > 0 ? number : 0; } function getAreaName(area) { return String((area && (area.areaName || area.label || area.name || area.title)) || "").trim(); } function getAreaChildren(area) { return area && Array.isArray(area.children) ? area.children : []; } function formatRegisterType(value) { if (value === "ONSITE") { return "现场登记"; } return "预约登记"; } function formatValidTime(start, end) { if (!start && !end) { return "-"; } return shortDateTime(start) + " 至 " + shortDateTime(end); } function shortDateTime(value) { return value ? String(value).slice(0, 16) : "-"; } function maskIdCard(value) { var text = String(value || ""); if (text.length < 8) { return text || "-"; } return text.slice(0, 4) + repeatString("*", text.length - 8) + text.slice(-4); } function repeatString(value, count) { var result = ""; for (var index = 0; index < count; index++) { result += value; } return result; } function toServerDateTime(value) { return value ? value.replace("T", " ") + ":00" : null; } function numberOrNull(value) { var number = Number(value); return number > 0 ? number : null; } 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]; }); } })();