routerAdd('GET', '/manage/dictionary-manage', function (e) {
    const html = `<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>字典管理</title>
    <script>
        (function () {
            var token = localStorage.getItem('pb_manage_token') || ''
            var isLoggedIn = localStorage.getItem('pb_manage_logged_in') === '1'
            if (!token || !isLoggedIn) {
                window.location.replace('/pb/manage/login')
            }
        })()
    </script>
    <style>
        * { box-sizing: border-box; }
        body { margin: 0; font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif; background: linear-gradient(180deg, #f3f6fb 0%, #eef2ff 100%); color: #1f2937; }
        .container { max-width: 1240px; margin: 0 auto; padding: 32px 20px 60px; }
        .topbar, .panel, .modal-card { background: rgba(255,255,255,0.96); border: 1px solid #e5e7eb; box-shadow: 0 18px 50px rgba(15, 23, 42, 0.08); }
        .topbar { border-radius: 24px; padding: 24px; margin-bottom: 24px; }
        .topbar h1 { margin: 0 0 8px; font-size: 30px; }
        .topbar p { margin: 0; color: #4b5563; line-height: 1.7; }
        .actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 18px; }
        .btn { border: none; border-radius: 12px; padding: 10px 16px; font-weight: 600; cursor: pointer; }
        .btn-primary { background: #2563eb; color: #fff; }
        .btn-secondary { background: #e0e7ff; color: #1e3a8a; }
        .btn-danger { background: #dc2626; color: #fff; }
        .btn-light { background: #f8fafc; color: #334155; border: 1px solid #dbe3f0; }
        .panel { border-radius: 24px; padding: 24px; }
        .toolbar { display: grid; grid-template-columns: 1.3fr 1fr 1fr auto auto; gap: 12px; margin-bottom: 18px; }
        input, textarea, select { width: 100%; border: 1px solid #cbd5e1; border-radius: 12px; padding: 10px 12px; font-size: 14px; background: #fff; }
        textarea { min-height: 88px; resize: vertical; }
        table { width: 100%; border-collapse: collapse; overflow: hidden; border-radius: 16px; }
        thead { background: #eff6ff; }
        th, td { padding: 14px 12px; border-bottom: 1px solid #e5e7eb; text-align: left; vertical-align: top; }
        th { font-size: 13px; color: #475569; }
        tr:hover td { background: #f8fafc; }
        .badge { display: inline-flex; align-items: center; padding: 4px 10px; border-radius: 999px; font-size: 12px; font-weight: 700; }
        .badge-on { background: #dcfce7; color: #166534; }
        .badge-off { background: #fee2e2; color: #991b1b; }
        .muted { color: #64748b; font-size: 13px; }
        .inline-input { min-width: 120px; }
        .link { color: #2563eb; text-decoration: none; font-weight: 600; }
        .status { margin: 14px 0 0; min-height: 24px; font-size: 14px; }
        .status.success { color: #15803d; }
        .status.error { color: #b91c1c; }
        .auth-box { display: grid; grid-template-columns: 1fr auto; gap: 12px; margin-top: 18px; }
        .modal { position: fixed; inset: 0; display: none; align-items: center; justify-content: center; background: rgba(15, 23, 42, 0.4); padding: 20px; }
        .modal.show { display: flex; }
        .modal-card { width: min(920px, 100%); border-radius: 24px; padding: 24px; max-height: 90vh; overflow: auto; }
        .modal-header { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 16px; }
        .modal-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
        .full { grid-column: 1 / -1; }
        .item-table { margin-top: 16px; border: 1px solid #e5e7eb; border-radius: 16px; overflow: hidden; }
        .item-table table { border-radius: 0; }
        .modal-actions { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 12px; margin-top: 16px; }
        .empty { text-align: center; padding: 32px 16px; color: #64748b; }
        @media (max-width: 960px) {
            .toolbar, .auth-box, .modal-grid { grid-template-columns: 1fr; }
            table, thead, tbody, th, td, tr { display: block; }
            thead { display: none; }
            tr { margin-bottom: 14px; border: 1px solid #e5e7eb; border-radius: 16px; overflow: hidden; }
            td { display: flex; justify-content: space-between; gap: 12px; }
            td::before { content: attr(data-label); font-weight: 700; color: #475569; }
        }
    </style>
</head>
<body>
    <div class="container">
        <section class="topbar">
            <h1>字典管理</h1>
            <p>页面仅允许 ManagePlatform 用户操作。当前请求会自动使用登录页保存到 localStorage 的 token。</p>
            <div class="actions">
                <a class="btn btn-light" href="/pb/manage">返回主页</a>
                <a class="btn btn-light" href="/pb/manage/login">登录页</a>
                <button class="btn btn-primary" id="createBtn" type="button">新增字典</button>
                <button class="btn btn-danger" id="logoutBtn" type="button">退出登录</button>
            </div>
            <div class="status" id="status"></div>
        </section>

        <section class="panel">
            <div class="toolbar">
                <input id="keywordInput" placeholder="按 dict_name 模糊搜索" />
                <input id="detailInput" placeholder="查询指定 dict_name" />
                <button class="btn btn-secondary" id="listBtn" type="button">查询全部</button>
                <button class="btn btn-light" id="detailBtn" type="button">查询指定</button>
                <button class="btn btn-light" id="resetBtn" type="button">重置</button>
            </div>
            <div style="overflow:auto;">
                <table>
                    <thead>
                        <tr>
                            <th>dict_name</th>
                            <th>启用</th>
                            <th>备注</th>
                            <th>parent_id</th>
                            <th>枚举项</th>
                            <th>创建时间</th>
                            <th>操作</th>
                        </tr>
                    </thead>
                    <tbody id="tableBody">
                        <tr><td colspan="7" class="empty">暂无数据，请先查询。</td></tr>
                    </tbody>
                </table>
            </div>
        </section>
    </div>

    <div class="modal" id="editorModal">
        <div class="modal-card">
            <div class="modal-header">
                <div>
                    <h2 id="modalTitle" style="margin:0;">新增字典</h2>
                    <div class="muted">三个聚合字段将以 JSON 字符串形式保存，并在读取时自动还原为 items。</div>
                </div>
                <button class="btn btn-light" id="closeModalBtn" type="button">关闭</button>
            </div>
            <div class="modal-grid">
                <div>
                    <label>dict_name</label>
                    <input id="dictNameInput" />
                </div>
                <div>
                    <label>dict_word_parent_id</label>
                    <input id="parentIdInput" />
                </div>
                <div>
                    <label>dict_word_is_enabled</label>
                    <select id="enabledInput">
                        <option value="true">启用</option>
                        <option value="false">禁用</option>
                    </select>
                </div>
                <div class="full">
                    <label>dict_word_remark</label>
                    <textarea id="remarkInput"></textarea>
                </div>
            </div>

            <div class="item-table">
                <table>
                    <thead>
                        <tr>
                            <th>枚举值</th>
                            <th>描述</th>
                            <th>排序</th>
                            <th>操作</th>
                        </tr>
                    </thead>
                    <tbody id="itemsBody"></tbody>
                </table>
            </div>

            <div class="modal-actions">
                <div>
                    <button class="btn btn-secondary" id="addItemBtn" type="button">新增枚举项</button>
                </div>
                <div>
                    <button class="btn btn-light" id="cancelBtn" type="button">取消</button>
                    <button class="btn btn-primary" id="saveBtn" type="button">保存</button>
                </div>
            </div>
        </div>
    </div>

    <script>
        const API_BASE = '/pb/api'
        const tokenKey = 'pb_manage_token'
        const state = {
            list: [],
            mode: 'create',
            editingOriginalName: '',
            items: [],
        }

        const statusEl = document.getElementById('status')
        const keywordInput = document.getElementById('keywordInput')
        const detailInput = document.getElementById('detailInput')
        const tableBody = document.getElementById('tableBody')
        const editorModal = document.getElementById('editorModal')
        const modalTitle = document.getElementById('modalTitle')
        const dictNameInput = document.getElementById('dictNameInput')
        const parentIdInput = document.getElementById('parentIdInput')
        const enabledInput = document.getElementById('enabledInput')
        const remarkInput = document.getElementById('remarkInput')
        const itemsBody = document.getElementById('itemsBody')

        function setStatus(message, type) {
            statusEl.textContent = message || ''
            statusEl.className = 'status' + (type ? ' ' + type : '')
        }

        function getToken() {
            return localStorage.getItem(tokenKey) || ''
        }

        async function request(url, payload) {
            const token = getToken()
            if (!token) {
                localStorage.removeItem('pb_manage_logged_in')
                window.location.replace('/pb/manage/login')
                throw new Error('登录状态已失效，请重新登录')
            }

            const res = await fetch(url, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    Authorization: 'Bearer ' + token,
                },
                body: JSON.stringify(payload || {}),
            })

            const data = await res.json()
            if (!res.ok || data.code >= 400) {
                if (res.status === 401 || res.status === 403 || data.code === 401 || data.code === 403) {
                    localStorage.removeItem('pb_manage_token')
                    localStorage.removeItem('pb_manage_logged_in')
                    localStorage.removeItem('pb_manage_login_account')
                    localStorage.removeItem('pb_manage_login_time')
                    window.location.replace('/pb/manage/login')
                }
                throw new Error(data.msg || '请求失败')
            }

            return data.data
        }

        function escapeHtml(value) {
            return String(value || '')
                .replace(/&/g, '&amp;')
                .replace(/</g, '&lt;')
                .replace(/>/g, '&gt;')
                .replace(/"/g, '&quot;')
                .replace(/'/g, '&#39;')
        }

        function renderItemsPreview(items) {
            if (!items || !items.length) return '<span class="muted">无</span>'
            return items.map(function (item) {
                return '<div><strong>' + escapeHtml(item.enum) + '</strong> → ' + escapeHtml(item.description) + '（排序 ' + escapeHtml(item.sortOrder) + '）</div>'
            }).join('')
        }

        function renderTable(list) {
            if (!list.length) {
                tableBody.innerHTML = '<tr><td colspan="7" class="empty">暂无匹配数据。</td></tr>'
                return
            }

            tableBody.innerHTML = list.map(function (item) {
                const enabledClass = item.dict_word_is_enabled ? 'badge badge-on' : 'badge badge-off'
                const enabledText = item.dict_word_is_enabled ? '启用' : '禁用'
                return '<tr>'
                    + '<td data-label="dict_name"><input class="inline-input" data-field="dict_name" data-name="' + escapeHtml(item.dict_name) + '" value="' + escapeHtml(item.dict_name) + '" /></td>'
                    + '<td data-label="启用"><select class="inline-input" data-field="dict_word_is_enabled" data-name="' + escapeHtml(item.dict_name) + '"><option value="true"' + (item.dict_word_is_enabled ? ' selected' : '') + '>启用</option><option value="false"' + (!item.dict_word_is_enabled ? ' selected' : '') + '>禁用</option></select><div class="' + enabledClass + '" style="margin-top:8px;">' + enabledText + '</div></td>'
                    + '<td data-label="备注"><textarea class="inline-input" data-field="dict_word_remark" data-name="' + escapeHtml(item.dict_name) + '">' + escapeHtml(item.dict_word_remark) + '</textarea></td>'
                    + '<td data-label="parent_id"><input class="inline-input" data-field="dict_word_parent_id" data-name="' + escapeHtml(item.dict_name) + '" value="' + escapeHtml(item.dict_word_parent_id) + '" /></td>'
                    + '<td data-label="枚举项">' + renderItemsPreview(item.items) + '</td>'
                    + '<td data-label="创建时间"><span class="muted">' + escapeHtml(item.created) + '</span></td>'
                    + '<td data-label="操作">'
                    + '<div style="display:flex;flex-wrap:wrap;gap:8px;">'
                    + '<button class="btn btn-light" type="button" onclick="window.__editItems(\\'' + encodeURIComponent(item.dict_name) + '\\')">编辑枚举</button>'
                    + '<button class="btn btn-secondary" type="button" onclick="window.__saveInline(\\'' + encodeURIComponent(item.dict_name) + '\\')">保存行</button>'
                    + '<button class="btn btn-danger" type="button" onclick="window.__deleteRow(\\'' + encodeURIComponent(item.dict_name) + '\\')">删除</button>'
                    + '</div>'
                    + '</td>'
                    + '</tr>'
            }).join('')
        }

        function openModal(mode, record) {
            state.mode = mode
            state.editingOriginalName = record ? record.dict_name : ''
            modalTitle.textContent = mode === 'create' ? '新增字典' : '编辑枚举项'
            dictNameInput.value = record ? record.dict_name : ''
            parentIdInput.value = record ? (record.dict_word_parent_id || '') : ''
            enabledInput.value = record && !record.dict_word_is_enabled ? 'false' : 'true'
            remarkInput.value = record ? (record.dict_word_remark || '') : ''
            state.items = record && Array.isArray(record.items) && record.items.length
                ? record.items.map(function (item) { return { enum: item.enum, description: item.description, sortOrder: item.sortOrder } })
                : [{ enum: '', description: '', sortOrder: 1 }]
            renderItemsEditor()
            editorModal.classList.add('show')
        }

        function closeModal() {
            editorModal.classList.remove('show')
        }

        function renderItemsEditor() {
            itemsBody.innerHTML = state.items.map(function (item, index) {
                return '<tr>'
                    + '<td><input data-item-field="enum" data-index="' + index + '" value="' + escapeHtml(item.enum) + '" /></td>'
                    + '<td><input data-item-field="description" data-index="' + index + '" value="' + escapeHtml(item.description) + '" /></td>'
                    + '<td><input type="number" data-item-field="sortOrder" data-index="' + index + '" value="' + escapeHtml(item.sortOrder) + '" /></td>'
                    + '<td><button class="btn btn-danger" type="button" onclick="window.__removeItem(' + index + ')">删除</button></td>'
                    + '</tr>'
            }).join('')
        }

        function collectItemsFromEditor() {
            const rows = Array.from(itemsBody.querySelectorAll('tr'))
            return rows.map(function (row, index) {
                const enumInput = row.querySelector('[data-item-field="enum"]')
                const descriptionInput = row.querySelector('[data-item-field="description"]')
                const sortOrderInput = row.querySelector('[data-item-field="sortOrder"]')
                return {
                    enum: enumInput.value.trim(),
                    description: descriptionInput.value.trim(),
                    sortOrder: Number(sortOrderInput.value || index + 1),
                }
            })
        }

        async function loadList() {
            setStatus('正在查询字典列表...', '')
            try {
                const data = await request(API_BASE + '/dictionary/list', { keyword: keywordInput.value.trim() })
                state.list = data.items || []
                renderTable(state.list)
                setStatus('查询成功，共 ' + state.list.length + ' 条。', 'success')
            } catch (err) {
                setStatus(err.message || '查询失败', 'error')
            }
        }

        async function loadDetail() {
            const dictName = detailInput.value.trim()
            if (!dictName) {
                setStatus('请输入 dict_name', 'error')
                return
            }

            setStatus('正在查询字典详情...', '')
            try {
                const data = await request(API_BASE + '/dictionary/detail', { dict_name: dictName })
                state.list = [data]
                renderTable(state.list)
                setStatus('查询详情成功。', 'success')
            } catch (err) {
                setStatus(err.message || '查询失败', 'error')
            }
        }

        async function saveModalRecord() {
            const items = collectItemsFromEditor()
            const payload = {
                dict_name: dictNameInput.value.trim(),
                original_dict_name: state.editingOriginalName,
                dict_word_parent_id: parentIdInput.value.trim(),
                dict_word_is_enabled: enabledInput.value === 'true',
                dict_word_remark: remarkInput.value.trim(),
                items: items,
            }

            setStatus('正在保存字典...', '')
            try {
                await request(state.mode === 'create' ? API_BASE + '/dictionary/create' : API_BASE + '/dictionary/update', payload)
                closeModal()
                await loadList()
                setStatus(state.mode === 'create' ? '新增成功。' : '修改成功。', 'success')
            } catch (err) {
                setStatus(err.message || '保存失败', 'error')
            }
        }

        async function saveInline(dictName) {
            const targetName = decodeURIComponent(dictName)
            const row = Array.from(tableBody.querySelectorAll('[data-name]')).filter(function (el) {
                return el.getAttribute('data-name') === targetName
            })
            const record = state.list.find(function (item) { return item.dict_name === targetName })

            if (!record) {
                setStatus('未找到对应记录', 'error')
                return
            }

            const payload = {
                original_dict_name: targetName,
                dict_name: (row.find(function (el) { return el.getAttribute('data-field') === 'dict_name' }) || {}).value || targetName,
                dict_word_is_enabled: ((row.find(function (el) { return el.getAttribute('data-field') === 'dict_word_is_enabled' }) || {}).value || 'true') === 'true',
                dict_word_remark: (row.find(function (el) { return el.getAttribute('data-field') === 'dict_word_remark' }) || {}).value || '',
                dict_word_parent_id: (row.find(function (el) { return el.getAttribute('data-field') === 'dict_word_parent_id' }) || {}).value || '',
                items: record.items || [],
            }

            setStatus('正在保存行数据...', '')
            try {
                await request(API_BASE + '/dictionary/update', payload)
                await loadList()
                setStatus('行内保存成功。', 'success')
            } catch (err) {
                setStatus(err.message || '保存失败', 'error')
            }
        }

        async function deleteRow(dictName) {
            const targetName = decodeURIComponent(dictName)
            if (!window.confirm('确认删除字典「' + targetName + '」吗？此操作不可恢复。')) {
                return
            }

            setStatus('正在删除字典...', '')
            try {
                await request(API_BASE + '/dictionary/delete', { dict_name: targetName })
                await loadList()
                setStatus('删除成功。', 'success')
            } catch (err) {
                setStatus(err.message || '删除失败', 'error')
            }
        }

        window.__editItems = function (dictName) {
            const targetName = decodeURIComponent(dictName)
            const record = state.list.find(function (item) { return item.dict_name === targetName })
            if (!record) {
                setStatus('未找到对应字典', 'error')
                return
            }
            openModal('edit', record)
        }

        window.__saveInline = saveInline
        window.__deleteRow = deleteRow
        window.__removeItem = function (index) {
            state.items.splice(index, 1)
            if (!state.items.length) {
                state.items.push({ enum: '', description: '', sortOrder: 1 })
            }
            renderItemsEditor()
        }

        document.getElementById('listBtn').addEventListener('click', loadList)
        document.getElementById('detailBtn').addEventListener('click', loadDetail)
        document.getElementById('resetBtn').addEventListener('click', function () {
            keywordInput.value = ''
            detailInput.value = ''
            state.list = []
            renderTable([])
            setStatus('已重置查询条件。', 'success')
        })
        document.getElementById('createBtn').addEventListener('click', function () { openModal('create') })
        document.getElementById('closeModalBtn').addEventListener('click', closeModal)
        document.getElementById('cancelBtn').addEventListener('click', closeModal)
        document.getElementById('addItemBtn').addEventListener('click', function () {
            state.items.push({ enum: '', description: '', sortOrder: state.items.length + 1 })
            renderItemsEditor()
        })
        document.getElementById('saveBtn').addEventListener('click', saveModalRecord)
        document.getElementById('logoutBtn').addEventListener('click', function () {
            localStorage.removeItem('pb_manage_token')
            localStorage.removeItem('pb_manage_logged_in')
            localStorage.removeItem('pb_manage_login_account')
            localStorage.removeItem('pb_manage_login_time')
            window.location.replace('/pb/manage/login')
        })
    </script>
</body>
</html>`

    return e.html(200, html)
})