fix: 修复设备管理页面前后端分页混合导致的数据闪烁问题
- 移除 append/loadMore 模式,改为纯后端分页 - 删除前端 slice useEffect,翻页时只调用后端请求 - 清理 loadMoreRef/hasMoreRef/deviceLoadingMore 等无用变量 - 删除 currentPageDevices state,allDevices 即当前页数据
This commit is contained in:
@@ -118,7 +118,6 @@ function DeviceManagement() {
|
|||||||
const [batchStatusLoading, setBatchStatusLoading] = useState(false);
|
const [batchStatusLoading, setBatchStatusLoading] = useState(false);
|
||||||
|
|
||||||
const [exportModalVisible, setExportModalVisible] = useState(false);
|
const [exportModalVisible, setExportModalVisible] = useState(false);
|
||||||
const [currentPageDevices, setCurrentPageDevices] = useState([]);
|
|
||||||
|
|
||||||
const [selectedDevices, setSelectedDevices] = useState([]);
|
const [selectedDevices, setSelectedDevices] = useState([]);
|
||||||
const [selectAll, setSelectAll] = useState(false);
|
const [selectAll, setSelectAll] = useState(false);
|
||||||
@@ -127,19 +126,10 @@ function DeviceManagement() {
|
|||||||
|
|
||||||
const debouncedKeyword = useDebounce(keyword, DEBOUNCE_DELAY);
|
const debouncedKeyword = useDebounce(keyword, DEBOUNCE_DELAY);
|
||||||
|
|
||||||
const loadMoreRef = useRef(null);
|
|
||||||
const hasMoreRef = useRef(true);
|
|
||||||
const isLoadingRef = useRef(false);
|
|
||||||
const [deviceLoadingMore, setDeviceLoadingMore] = useState(false);
|
|
||||||
|
|
||||||
const fetchDevices = useCallback(
|
const fetchDevices = useCallback(
|
||||||
async (page = 1, pageSize = 50, append = false) => {
|
async (page = 1, pageSize = 50) => {
|
||||||
try {
|
try {
|
||||||
if (append) {
|
|
||||||
setDeviceLoadingMore(true);
|
|
||||||
} else {
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
}
|
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
page,
|
page,
|
||||||
@@ -155,20 +145,13 @@ function DeviceManagement() {
|
|||||||
const { devices: deviceList, total } = response.data;
|
const { devices: deviceList, total } = response.data;
|
||||||
|
|
||||||
const processedDevices = deviceList.map(processDeviceData);
|
const processedDevices = deviceList.map(processDeviceData);
|
||||||
|
|
||||||
if (append) {
|
|
||||||
setAllDevices(prev => [...prev, ...processedDevices]);
|
|
||||||
} else {
|
|
||||||
setAllDevices(processedDevices);
|
setAllDevices(processedDevices);
|
||||||
}
|
|
||||||
setPagination(prev => ({ ...prev, current: page, pageSize, total }));
|
setPagination(prev => ({ ...prev, current: page, pageSize, total }));
|
||||||
hasMoreRef.current = page * pageSize < total;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error('获取设备列表失败');
|
message.error('获取设备列表失败');
|
||||||
console.error('获取设备列表失败:', error);
|
console.error('获取设备列表失败:', error);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setDeviceLoadingMore(false);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[debouncedKeyword, status, type, roomId, rackId]
|
[debouncedKeyword, status, type, roomId, rackId]
|
||||||
@@ -260,72 +243,6 @@ function DeviceManagement() {
|
|||||||
}
|
}
|
||||||
}, [searchParams, setSearchParams]);
|
}, [searchParams, setSearchParams]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (allDevices.length > 0) {
|
|
||||||
const start = (pagination.current - 1) * pagination.pageSize;
|
|
||||||
const end = start + pagination.pageSize;
|
|
||||||
const currentPageData = allDevices.slice(start, end);
|
|
||||||
setCurrentPageDevices(currentPageData);
|
|
||||||
} else {
|
|
||||||
setCurrentPageDevices([]);
|
|
||||||
}
|
|
||||||
}, [allDevices, pagination.current, pagination.pageSize]);
|
|
||||||
|
|
||||||
const handleLoadMoreDevices = useCallback(() => {
|
|
||||||
if (!hasMoreRef.current || isLoadingRef.current || deviceLoadingMore) return;
|
|
||||||
if (
|
|
||||||
debouncedKeyword ||
|
|
||||||
status !== 'all' ||
|
|
||||||
type !== 'all' ||
|
|
||||||
roomId !== 'all' ||
|
|
||||||
rackId !== 'all'
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
isLoadingRef.current = true;
|
|
||||||
const nextPage = pagination.current + 1;
|
|
||||||
fetchDevices(nextPage, pagination.pageSize, true).then(() => {
|
|
||||||
isLoadingRef.current = false;
|
|
||||||
});
|
|
||||||
}, [
|
|
||||||
pagination.current,
|
|
||||||
pagination.pageSize,
|
|
||||||
debouncedKeyword,
|
|
||||||
status,
|
|
||||||
type,
|
|
||||||
roomId,
|
|
||||||
rackId,
|
|
||||||
deviceLoadingMore,
|
|
||||||
fetchDevices,
|
|
||||||
]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!loadMoreRef.current) return;
|
|
||||||
|
|
||||||
const observer = new IntersectionObserver(
|
|
||||||
entries => {
|
|
||||||
if (entries[0].isIntersecting && hasMoreRef.current && !deviceLoadingMore) {
|
|
||||||
if (
|
|
||||||
!debouncedKeyword &&
|
|
||||||
status === 'all' &&
|
|
||||||
type === 'all' &&
|
|
||||||
roomId === 'all' &&
|
|
||||||
rackId === 'all'
|
|
||||||
) {
|
|
||||||
handleLoadMoreDevices();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ threshold: 0.1, rootMargin: '100px' }
|
|
||||||
);
|
|
||||||
|
|
||||||
observer.observe(loadMoreRef.current);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
observer.disconnect();
|
|
||||||
};
|
|
||||||
}, [handleLoadMoreDevices, deviceLoadingMore, debouncedKeyword, status, type, roomId, rackId]);
|
|
||||||
|
|
||||||
const showModal = (device = null) => {
|
const showModal = (device = null) => {
|
||||||
setEditingDevice(device);
|
setEditingDevice(device);
|
||||||
setModalVisible(true);
|
setModalVisible(true);
|
||||||
@@ -347,7 +264,7 @@ function DeviceManagement() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setModalVisible(false);
|
setModalVisible(false);
|
||||||
fetchDevices(1, pagination.pageSize, false);
|
fetchDevices(1, pagination.pageSize);
|
||||||
setEditingDevice(null);
|
setEditingDevice(null);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMsg = error.response?.data?.error || error.message || '未知错误';
|
const errorMsg = error.response?.data?.error || error.message || '未知错误';
|
||||||
@@ -385,13 +302,7 @@ function DeviceManagement() {
|
|||||||
|
|
||||||
const handleTableChange = newPagination => {
|
const handleTableChange = newPagination => {
|
||||||
setPagination(newPagination);
|
setPagination(newPagination);
|
||||||
|
fetchDevices(newPagination.current, newPagination.pageSize);
|
||||||
const start = (newPagination.current - 1) * newPagination.pageSize;
|
|
||||||
const end = start + newPagination.pageSize;
|
|
||||||
const currentPageData = allDevices.slice(start, end);
|
|
||||||
setCurrentPageDevices(currentPageData);
|
|
||||||
|
|
||||||
fetchDevices(newPagination.current, newPagination.pageSize, false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleBatchDelete = async () => {
|
const handleBatchDelete = async () => {
|
||||||
@@ -409,7 +320,7 @@ function DeviceManagement() {
|
|||||||
message.success(response.data.message || '批量删除成功');
|
message.success(response.data.message || '批量删除成功');
|
||||||
setSelectedDevices([]);
|
setSelectedDevices([]);
|
||||||
setSelectAll(false);
|
setSelectAll(false);
|
||||||
fetchDevices(1, 50, false);
|
fetchDevices(1, 50);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error('批量删除失败');
|
message.error('批量删除失败');
|
||||||
console.error('批量删除设备失败:', error);
|
console.error('批量删除设备失败:', error);
|
||||||
@@ -436,7 +347,7 @@ function DeviceManagement() {
|
|||||||
message.success(response.data.message || '成功删除所有设备');
|
message.success(response.data.message || '成功删除所有设备');
|
||||||
setSelectedDevices([]);
|
setSelectedDevices([]);
|
||||||
setSelectAll(false);
|
setSelectAll(false);
|
||||||
fetchDevices(1, 50, false);
|
fetchDevices(1, 50);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error('删除所有设备失败');
|
message.error('删除所有设备失败');
|
||||||
console.error('删除所有设备失败:', error);
|
console.error('删除所有设备失败:', error);
|
||||||
@@ -464,7 +375,7 @@ function DeviceManagement() {
|
|||||||
message.success(response.data.message || '设备已标记为空闲');
|
message.success(response.data.message || '设备已标记为空闲');
|
||||||
setSelectedDevices([]);
|
setSelectedDevices([]);
|
||||||
setSelectAll(false);
|
setSelectAll(false);
|
||||||
fetchDevices(1, 50, false);
|
fetchDevices(1, 50);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error(error.response?.data?.error || '标记为空闲失败');
|
message.error(error.response?.data?.error || '标记为空闲失败');
|
||||||
console.error('标记为空闲失败:', error);
|
console.error('标记为空闲失败:', error);
|
||||||
@@ -484,7 +395,7 @@ function DeviceManagement() {
|
|||||||
try {
|
try {
|
||||||
await axios.delete(`/api/devices/${deviceId}`);
|
await axios.delete(`/api/devices/${deviceId}`);
|
||||||
message.success('设备删除成功');
|
message.success('设备删除成功');
|
||||||
fetchDevices(1, pagination.pageSize, false);
|
fetchDevices(1, pagination.pageSize);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error('设备删除失败');
|
message.error('设备删除失败');
|
||||||
console.error('设备删除失败:', error);
|
console.error('设备删除失败:', error);
|
||||||
@@ -530,7 +441,7 @@ function DeviceManagement() {
|
|||||||
setBatchStatusModalVisible(false);
|
setBatchStatusModalVisible(false);
|
||||||
setSelectedDevices([]);
|
setSelectedDevices([]);
|
||||||
setSelectAll(false);
|
setSelectAll(false);
|
||||||
fetchDevices(1, pagination.pageSize, false);
|
fetchDevices(1, pagination.pageSize);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error('批量状态变更失败');
|
message.error('批量状态变更失败');
|
||||||
console.error('批量状态变更失败:', error);
|
console.error('批量状态变更失败:', error);
|
||||||
@@ -552,7 +463,7 @@ function DeviceManagement() {
|
|||||||
if (scope === 'selected') {
|
if (scope === 'selected') {
|
||||||
deviceIds = selectedDevices;
|
deviceIds = selectedDevices;
|
||||||
} else if (scope === 'currentPage') {
|
} else if (scope === 'currentPage') {
|
||||||
deviceIds = currentPageDevices.map(device => device.deviceId);
|
deviceIds = allDevices.map(device => device.deviceId);
|
||||||
} else if (scope === 'all') {
|
} else if (scope === 'all') {
|
||||||
deviceIds = allDevices.map(device => device.deviceId);
|
deviceIds = allDevices.map(device => device.deviceId);
|
||||||
}
|
}
|
||||||
@@ -621,7 +532,7 @@ function DeviceManagement() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
fetchDevices(1, pagination.pageSize, false);
|
fetchDevices(1, pagination.pageSize);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
let errorMessage = '导入失败';
|
let errorMessage = '导入失败';
|
||||||
@@ -1153,7 +1064,7 @@ function DeviceManagement() {
|
|||||||
</Space>
|
</Space>
|
||||||
<Button
|
<Button
|
||||||
icon={<ReloadOutlined />}
|
icon={<ReloadOutlined />}
|
||||||
onClick={() => fetchDevices(1, pagination.pageSize, false)}
|
onClick={() => fetchDevices(1, pagination.pageSize)}
|
||||||
style={{
|
style={{
|
||||||
borderRadius: designTokens.borderRadius.medium,
|
borderRadius: designTokens.borderRadius.medium,
|
||||||
border: `1px solid ${designTokens.colors.border.light}`,
|
border: `1px solid ${designTokens.colors.border.light}`,
|
||||||
@@ -1345,21 +1256,6 @@ function DeviceManagement() {
|
|||||||
return index % 2 === 0 ? 'ant-table-row-even' : 'ant-table-row-odd';
|
return index % 2 === 0 ? 'ant-table-row-even' : 'ant-table-row-odd';
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{hasMoreRef.current &&
|
|
||||||
!debouncedKeyword &&
|
|
||||||
status === 'all' &&
|
|
||||||
type === 'all' &&
|
|
||||||
roomId === 'all' &&
|
|
||||||
rackId === 'all' && (
|
|
||||||
<div ref={loadMoreRef} style={{ textAlign: 'center', padding: '20px' }}>
|
|
||||||
{deviceLoadingMore && <Spin tip="加载更多设备..." />}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{!hasMoreRef.current && allDevices.length > 0 && (
|
|
||||||
<div style={{ textAlign: 'center', padding: '16px', color: '#999' }}>
|
|
||||||
已加载全部 {pagination.total} 个设备
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
@@ -1397,7 +1293,7 @@ function DeviceManagement() {
|
|||||||
<ExportModal
|
<ExportModal
|
||||||
visible={exportModalVisible}
|
visible={exportModalVisible}
|
||||||
selectedDevices={selectedDevices}
|
selectedDevices={selectedDevices}
|
||||||
currentPageDevices={currentPageDevices}
|
currentPageDevices={allDevices}
|
||||||
allDevices={allDevices}
|
allDevices={allDevices}
|
||||||
onExport={handleEnhancedExport}
|
onExport={handleEnhancedExport}
|
||||||
onCancel={() => setExportModalVisible(false)}
|
onCancel={() => setExportModalVisible(false)}
|
||||||
|
|||||||
Reference in New Issue
Block a user