feat(forum): rebuild forum home page

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
kate522andClaude Fable 5 committed 2026-11-11 14:41:02 +08:00
1 parent 51d5b8a2a1
commit 024ecddfed
16 files changed
+1061 -198

No files matched your search

+102 -15
View File
@@ -23,8 +23,7 @@
else if (value) window.alert(value)
}
const modal = (title, fields, submitText = '确认') => new Promise((resolve) => {
const dialog = document.createElement('dialog')
const modal = (title, fields, submitText = '确认') => new Promise((resolve) => { const dialog = document.createElement('dialog')
dialog.className = 'jio-forum__dialog'
const form = document.createElement('form')
form.innerHTML = '<h2></h2><div class="jio-forum__dialog-fields"></div><div class="jio-forum__dialog-actions"><button type="button" data-cancel>取消</button><button type="submit" class="jio-forum__primary"></button></div>'
@@ -172,7 +171,13 @@
return
}
const open = event.target.closest('[data-forum-open]')
if (open) { root.querySelector(`[data-forum-dialog="${open.dataset.forumOpen}"]`)?.showModal(); return }
if (open) {
const dialog = root.querySelector(`[data-forum-dialog="${open.dataset.forumOpen}"]`)
const category = dialog?.querySelector('select[name="category_id"]')
if (category && open.dataset.category) category.value = open.dataset.category
dialog?.showModal()
return
}
const close = event.target.closest('[data-forum-close]')
if (close) { close.closest('dialog')?.close(); return }
const reply = event.target.closest('[data-forum-reply-to]')
@@ -193,7 +198,7 @@
if (action === 'plate-unlock') { await checkout('plate', id); return }
if (action === 'plate-audit') {
result = await send(`plates/${id}/audit`, 'POST', { status: 'publish' })
button.closest('.jio-forum__pending > div')?.remove()
button.closest('.forum-pending-plate')?.remove()
} else if (action === 'plate-delete') {
const data = await modal('删除版块', [
{ name: 'posts_action', label: '关联帖子', type: 'select', options: [{ value: 'trash', label: '一并移入回收站' }, { value: 'move', label: '移动到其他版块' }] },
@@ -213,16 +218,6 @@
if (!window.confirm('确定删除此回复?')) return
result = await send(`replies/${id}/delete`, 'POST')
button.closest('.jio-forum__reply')?.remove()
} else if (action === 'term-edit') {
const name = button.closest('.jio-forum__term-row')?.querySelector('span')?.textContent || ''
const data = await modal('编辑分类', [{ name: 'name', label: '名称', value: name }], '保存')
if (!data) return
result = await send(`terms/${id}`, 'PATCH', data)
button.closest('.jio-forum__term-row').querySelector('span').textContent = data.name
} else if (action === 'term-delete') {
if (!window.confirm('确定删除这个分类?')) return
result = await send(`terms/${id}`, 'DELETE')
button.closest('.jio-forum__term-row')?.remove()
} else if (action === 'follow') {
try { result = await send(`plates/${id}/follow`, 'POST') }
catch (error) { if (error.message.includes('付费')) { await checkout('plate', id); return } throw error }
@@ -254,7 +249,17 @@
button.dataset.forumAction = 'restore'
button.textContent = '撤销删除'
} else if (action === 'adopt' || action === 'audit') window.location.reload()
else button.textContent = result.data?.label || result.message || button.textContent
else if (action === 'follow') {
const following = result.data?.following === true
button.classList.toggle('active', following)
const label = button.querySelector('text')
if (label) label.textContent = following ? '已关注' : '关注'
else button.textContent = result.data?.label || button.textContent
} else if (button.querySelector('text')) {
button.querySelector('text').textContent = result.data?.label || result.message || button.querySelector('text').textContent
} else {
button.textContent = result.data?.label || result.message || button.textContent
}
message(button, result.message || '')
} catch (error) {
message(button, error.message)
@@ -263,4 +268,86 @@
button.disabled = false
}
})
// 论坛首页栏目:点击切换 + 懒加载该栏目的 ajaxpager 内容
const refresh = (pane) => {
if (typeof window.auto_fun === 'function') window.auto_fun()
if (typeof window.jioInitForumMedia === 'function') window.jioInitForumMedia(pane)
}
const loadTab = async (pane, href) => {
if (!pane || pane.dataset.loaded === '1' || pane.dataset.loading === '1') return
pane.dataset.loading = '1'
pane.querySelector('.post_ajax_loader')?.classList.add('is-loading')
try {
const response = await fetch(href, { credentials: 'same-origin', headers: { 'X-Requested-With': 'XMLHttpRequest' } })
if (!response.ok) throw new Error('加载失败')
const doc = new DOMParser().parseFromString(await response.text(), 'text/html')
const source = doc.getElementById(pane.id)
if (!source) throw new Error('内容不存在')
pane.innerHTML = source.innerHTML
pane.dataset.loaded = '1'
refresh(pane)
} catch (error) {
pane.querySelector('.post_ajax_loader')?.classList.remove('is-loading')
message(pane, error.message)
} finally {
pane.dataset.loading = ''
}
}
const loadMore = async (link, pane) => {
if (pane.dataset.loading === '1') return
pane.dataset.loading = '1'
const more = link.closest('.forum-more')
const loader = [...pane.children].find((item) => item.classList.contains('post_ajax_loader'))
more?.classList.add('is-loading')
loader?.classList.add('is-loading')
try {
const response = await fetch(link.getAttribute('href'), { credentials: 'same-origin', headers: { 'X-Requested-With': 'XMLHttpRequest' } })
if (!response.ok) throw new Error('加载失败')
const doc = new DOMParser().parseFromString(await response.text(), 'text/html')
const source = doc.getElementById(pane.id)
if (!source) throw new Error('内容不存在')
const anchor = more || null
const incoming = [...source.children].filter((item) => !item.classList.contains('forum-tab-pane') && !item.classList.contains('forum-more') && !item.classList.contains('post_ajax_loader'))
incoming.forEach((item) => pane.insertBefore(item, anchor))
const next = source.querySelector('[data-forum-more]')
if (next && anchor) anchor.querySelector('[data-forum-more]')?.setAttribute('href', next.getAttribute('href'))
else anchor?.remove()
refresh(pane)
} catch (error) {
message(pane, error.message)
} finally {
pane.dataset.loading = ''
more?.classList.remove('is-loading')
loader?.classList.remove('is-loading')
}
}
root.addEventListener('click', (event) => {
const tab = event.target.closest('[data-forum-tab]')
if (tab) {
event.preventDefault()
const target = tab.getAttribute('data-forum-tab')
const pane = root.querySelector(target)
root.querySelectorAll('[data-forum-tab]').forEach((link) => link.closest('li')?.classList.toggle('active', link === tab))
root.querySelectorAll('.forum-tab-pane').forEach((item) => {
const current = `#${item.id}` === target
item.classList.toggle('active', current)
item.classList.toggle('in', current)
})
if (pane) {
loadTab(pane, pane.dataset.href || tab.getAttribute('href'))
if (pane.dataset.href && window.history?.replaceState) window.history.replaceState(null, document.title, pane.dataset.href)
}
return
}
const more = event.target.closest('[data-forum-more]')
if (more) {
event.preventDefault()
const pane = more.closest('.forum-tab-pane')
if (pane) loadMore(more, pane)
}
})
})()