v1.2.9 - 分页加载、用户编码管理、统计页优化

This commit is contained in:
甲辰生产 2026-03-22 21:20:47 +08:00
parent 784ea0b0ea
commit 8c101c36e1
5 changed files with 62 additions and 32 deletions

View File

@ -1 +1 @@
VERSION=v1.2.4
VERSION=1.2.9

View File

@ -1 +1 @@
VERSION=1.2.6
VERSION=1.2.9

View File

@ -126,7 +126,8 @@ export default function Admin() {
const updateData = {
username: editingUser.username,
email: editingUser.email,
role: editingUser.role
role: editingUser.role,
user_code: editingUser.user_code
}
//
@ -145,7 +146,9 @@ export default function Admin() {
if (!res.ok) {
const data = await res.json()
throw new Error(data.detail || '更新失败')
// : {error: {code, message}} : {detail}
const errorMsg = data.error?.message || data.detail || '更新失败'
throw new Error(errorMsg)
}
alert('更新成功!')
@ -362,6 +365,18 @@ export default function Admin() {
<option value="admin">管理员</option>
</select>
</div>
<div style={{ marginBottom: '16px' }}>
<label style={{ display: 'block', color: '#94a3b8', fontSize: '13px', marginBottom: '6px' }}>用户编码 <span style={{ color: '#64748b', fontSize: '12px' }}>0001</span></label>
<input
type="text"
value={editingUser.user_code || ''}
onChange={(e) => setEditingUser({ ...editingUser, user_code: e.target.value })}
placeholder="4位数字0001"
maxLength={4}
style={{ width: '100%', padding: '10px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '8px', color: '#fff' }}
/>
</div>
</div>
{/* 第二部分:修改密码 */}

View File

@ -16,8 +16,9 @@ export default function List() {
const [search, setSearch] = useState('')
const [isAdmin, setIsAdmin] = useState(false)
const [page, setPage] = useState(1)
const [hasMore, setHasMore] = useState(true)
const [loadingMore, setLoadingMore] = useState(false)
const [hasMore, setHasMore] = useState(true)
const [total, setTotal] = useState(0)
useEffect(() => {
//
@ -62,27 +63,16 @@ export default function List() {
}
}, [])
//
useEffect(() => {
const handleScroll = () => {
if (loading || loadingMore || !hasMore) return
const scrollTop = window.scrollY || document.documentElement.scrollTop
const scrollHeight = document.documentElement.scrollHeight
const clientHeight = document.documentElement.clientHeight
if (scrollTop + clientHeight >= scrollHeight - 100) {
fetchCollections(false)
const fetchCollections = async (reset = true) => {
if (!reset && (loading || loadingMore || !hasMore)) return
if (reset) {
setLoading(true)
} else {
setLoadingMore(true)
}
}
window.addEventListener('scroll', handleScroll)
return () => window.removeEventListener('scroll', handleScroll)
}, [loading, loadingMore, hasMore, page])
const fetchCollections = async () => {
// set loading based on reset
const token = localStorage.getItem('token')
try {
const res = await fetch('/api/collections?page=1&limit=50', {
const res = await fetch(`/api/collections?page=${page}&limit=50`, {
headers: token ? { 'Authorization': 'Bearer ' + token } : {}
})
@ -100,6 +90,12 @@ export default function List() {
if (!Array.isArray(list) && list && Array.isArray(list.items)) {
list = list.items
}
//
if (data.pagination && data.pagination.total) {
setTotal(data.pagination.total)
} else if (reset) {
setTotal(list.length)
}
//
// IDURL
@ -139,14 +135,17 @@ export default function List() {
console.log(`筛选:${filterType} = ${filter}, 结果:${list.length}`)
}
//
if (page === 1) {
if (reset) {
setCollections(list || [])
setPage(1)
} else {
setCollections(prev => [...prev, ...(list || [])])
setCollections(prev => [...prev, ...list])
}
if (list.length < 50) {
setHasMore(false)
} else {
setPage(p => p + 1)
}
setHasMore((list || []).length >= 50)
if (page > 1) setPage(p => p + 1)
} catch (e) {
console.error('Fetch collections error:', e)
//
@ -155,6 +154,7 @@ export default function List() {
window.location.hash = '#/login'
}
setLoading(false)
setLoadingMore(false)
}
const refresh = () => {
@ -166,6 +166,22 @@ export default function List() {
return () => { delete window.refreshList }
}, [])
//
useEffect(() => {
const handleScroll = () => {
if (loading || loadingMore || !hasMore) return
const scrollTop = window.scrollY || document.documentElement.scrollTop
const scrollHeight = document.documentElement.scrollHeight
const clientHeight = document.documentElement.clientHeight
if (scrollTop + clientHeight >= scrollHeight - 100) {
fetchCollections(false)
}
}
window.addEventListener('scroll', handleScroll)
return () => window.removeEventListener('scroll', handleScroll)
}, [loading, loadingMore, hasMore, page])
const goDetail = (id) => {
// URL
sessionStorage.setItem('lastListUrl', window.location.hash.substring(1))
@ -454,7 +470,7 @@ export default function List() {
<div style={{ minHeight: '100vh', overflowY: 'auto', background: '#0f172a', paddingBottom: '70px' }}>
<div style={{ padding: '20px', background: 'rgba(255,255,255,0.02)', borderBottom: '1px solid rgba(255,255,255,0.05)' }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '16px' }}>
<div style={{ color: '#fff', fontSize: '20px', }}>我的藏品 <span style={{ fontSize: '14px', color: '#fbbf24' }}>({filteredCollections.length})</span></div>
<div style={{ color: '#fff', fontSize: '20px', }}>我的藏品 <span style={{ fontSize: '14px', color: '#fbbf24' }}>({total || filteredCollections.length})</span></div>
<div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
{filter && filterType && (
<button

View File

@ -159,7 +159,7 @@ export default function Stats() {
<div style={{ background: 'rgba(255,255,255,0.03)', borderRadius: '12px', padding: '16px', marginBottom: '12px' }}>
<div style={{ color: '#fbbf24', fontSize: '14px', fontWeight: 'bold', marginBottom: '12px' }}>{title}</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '8px' }}>
{getSortedData(data, type).slice(0, 6).map((item, index) => {
{getSortedData(data, type).map((item, index) => {
const value = item[valueKey]
const label = getLabel(type, item[labelKey] || value)
const color = getColor(type, value)
@ -201,7 +201,6 @@ export default function Stats() {
</div>
{data.length > 6 && (
<div style={{ textAlign: 'center', color: '#94a3b8', fontSize: '12px', marginTop: '8px' }}>
{data.length}显示前 6
</div>
)}
</div>