fix: paginate operational list pages

This commit is contained in:
hectorzhao
2026-07-28 23:20:13 +08:00
parent 87c5b1eccc
commit b8560372cc
40 changed files with 1208 additions and 422 deletions
+18 -25
View File
@@ -1,4 +1,4 @@
import { useEffect, useMemo, useState } from 'react';
import { useEffect, useState } from 'react';
import { CheckCircle2, Copy, Eye, ExternalLink, FileText, Info, Pencil, Plus, Power, Search, Send, Trash2 } from 'lucide-react';
import { useNavigate } from 'react-router-dom';
import { adminApi, type AdminChannel, type ChannelConnectionLogResponse, type ChannelQualityStat, type ChannelTestResponse, type CmppConnectionState } from '@/api/adminApi';
@@ -497,42 +497,35 @@ export function AdminChannelsPage() {
const [logState, setLogState] = useState<ChannelLogState | null>(null);
const [logKeyword, setLogKeyword] = useState('');
const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
const pageSize = 10;
function loadChannels() {
Promise.all([adminApi.listChannels(), adminApi.getSendQuality()])
.then(async ([items, quality]) => {
const visibleChannels = items.filter((item) => item.status !== 'deleted');
function loadChannels(targetPage = page, filters = { keyword, carrier, status }) {
Promise.all([
adminApi.listChannelsPage({ keyword: filters.keyword.trim() || undefined, carrier: filters.carrier, status: filters.status, page: targetPage, pageSize }),
adminApi.getSendQuality(),
])
.then(async ([result, quality]) => {
const visibleChannels = result.items;
const connections = await Promise.all(visibleChannels.map((channel) =>
adminApi.listChannelConnections(channel.id).catch(() => [] as CmppConnectionState[]),
));
const qualityByChannel = new Map(quality.channels.map((item) => [item.channelId, item]));
setChannels(visibleChannels.map((item, index) => mapApiChannel(item, connections[index], qualityByChannel.get(item.id))));
setTotal(result.total);
setError('');
})
.catch((failure: Error) => setError(failure.message || '通道列表加载失败'));
}
useEffect(() => {
loadChannels();
}, []);
loadChannels(page);
}, [page]);
const filteredChannels = useMemo(
() => channels.filter((channel) => {
const matchesKeyword = !keyword || channel.name.includes(keyword);
const matchesCarrier = carrier === 'all' || channel.carrier === carrier;
const matchesStatus = status === 'all' || channel.status === status;
return matchesKeyword && matchesCarrier && matchesStatus;
}),
[carrier, channels, keyword, status],
);
const totalPages = Math.max(1, Math.ceil(filteredChannels.length / pageSize));
const filteredChannels = channels;
const totalPages = Math.max(1, Math.ceil(total / pageSize));
const currentPage = Math.min(page, totalPages);
const visibleChannels = filteredChannels.slice((currentPage - 1) * pageSize, currentPage * pageSize);
useEffect(() => {
setPage(1);
}, [carrier, channels.length, keyword, status]);
const visibleChannels = channels;
async function upsertChannel(nextChannel: SmsChannel) {
try {
@@ -617,8 +610,8 @@ export function AdminChannelsPage() {
<Select label="运营商" onChange={(event) => setCarrier(event.target.value)} options={carrierOptions} value={carrier} />
<Select label="当前状态" onChange={(event) => setStatus(event.target.value)} options={statusOptions} value={status} />
<div className="audit-filter-actions">
<Button icon={<Search size={16} />} onClick={() => void loadChannels()}></Button>
<Button onClick={() => { setKeyword(''); setCarrier('all'); setStatus('all'); }} variant="ghost"></Button>
<Button icon={<Search size={16} />} onClick={() => { setPage(1); void loadChannels(1); }}></Button>
<Button onClick={() => { setKeyword(''); setCarrier('all'); setStatus('all'); setPage(1); void loadChannels(1, { keyword: '', carrier: 'all', status: 'all' }); }} variant="ghost"></Button>
</div>
</div>
</div>
@@ -677,7 +670,7 @@ export function AdminChannelsPage() {
totalPages={totalPages}
onPageChange={setPage}
previousDisabled={currentPage <= 1}
total={filteredChannels.length}
total={total}
/>
</div>