(() => { const API_BASE = '/api/rational_lock' const DEFAULT_USERNAME = 'admin' const $ = id => document.getElementById(id) const now = () => Date.now() const uid = () => `${Date.now()}_${Math.random().toString(36).slice(2, 8)}` const emptyDB = () => ({ items: [], applications: [] }) const state = { db: emptyDB(), auth: { currentUser: '' }, ui: { editingId: '' } } const normalizeDB = value => { const base = value && typeof value === 'object' ? value : {} return { items: Array.isArray(base.items) ? base.items : [], applications: Array.isArray(base.applications) ? base.applications : [] } } const setAuthStatus = text => { $('auth-status').textContent = text || '' } const setFormStatus = text => { $('form-status').textContent = text || '' } const openModal = ({ title, message, okText = '知道了', cancelText = '', tone = 'normal' }) => { return new Promise(resolve => { const mask = $('app-modal') const titleBox = $('modal-title') const msgBox = $('modal-message') const actions = $('modal-actions') actions.innerHTML = '' titleBox.textContent = title || '' msgBox.textContent = message || '' const close = val => { mask.style.display = 'none' resolve(val) } if (cancelText) { const cancelBtn = document.createElement('button') cancelBtn.className = 'btn' cancelBtn.type = 'button' cancelBtn.textContent = cancelText cancelBtn.addEventListener('click', () => close(false)) actions.appendChild(cancelBtn) } const okBtn = document.createElement('button') okBtn.className = tone === 'primary' ? 'btn btn-primary' : 'btn' okBtn.type = 'button' okBtn.textContent = okText okBtn.addEventListener('click', () => close(true)) actions.appendChild(okBtn) mask.style.display = '' }) } const modalAlert = (title, message) => openModal({ title, message, okText: '我知道了' }) const modalConfirm = (title, message, okText = '继续', cancelText = '取消') => openModal({ title, message, okText, cancelText, tone: 'primary' }) const apiRequest = async (path, options = {}) => { const res = await fetch(`${API_BASE}${path}`, { method: options.method || 'GET', headers: { 'Content-Type': 'application/json', ...(options.headers || {}) }, body: options.body === undefined ? undefined : JSON.stringify(options.body), credentials: 'include' }) let payload = null try { payload = await res.json() } catch { payload = null } if (!res.ok) { const msg = payload && payload.error ? String(payload.error) : `http_${res.status}` throw new Error(msg) } return payload || {} } const renderAuth = () => { const user = state.auth.currentUser $('auth-login-box').style.display = user ? 'none' : '' $('auth-logged-box').style.display = user ? '' : 'none' $('app-content').style.display = user ? '' : 'none' $('auth-current-user').textContent = user ? `当前账号:${user}` : '' } const loadDB = async () => { if (!state.auth.currentUser) { state.db = emptyDB() return } const payload = await apiRequest('/data') state.db = normalizeDB(payload.db) } const saveDB = async () => { if (!state.auth.currentUser) return await apiRequest('/data', { method: 'PUT', body: { db: state.db } }) } const upsertItem = (name, category, note) => { const normalizedName = String(name || '').trim() if (!normalizedName) return null const normalizedCategory = String(category || '其他').trim() || '其他' const idx = state.db.items.findIndex(it => String(it.name || '').trim().toLowerCase() === normalizedName.toLowerCase() && String(it.category || '').trim() === normalizedCategory) if (idx >= 0) { const next = { ...state.db.items[idx], name: normalizedName, category: normalizedCategory, note: String(note || '').trim(), updatedAt: now() } state.db.items[idx] = next return next } const row = { id: uid(), name: normalizedName, category: normalizedCategory, note: String(note || '').trim(), createdAt: now() } state.db.items.unshift(row) return row } const renderLibrary = () => { $('btn-lib-add').textContent = state.ui.editingId ? '保存修改' : '加入物品库' $('btn-lib-cancel-edit').style.display = state.ui.editingId ? '' : 'none' const box = $('lib-list') box.innerHTML = '' if (!state.db.items.length) { box.textContent = '暂无物品库记录' return } state.db.items.forEach(it => { const row = document.createElement('div') row.className = 'row' row.innerHTML = `