diff --git a/Cunkebao/src/components/FriendSelection/TwoColumnSelection.tsx b/Cunkebao/src/components/FriendSelection/TwoColumnSelection.tsx
index 58e99c1a..78858b89 100644
--- a/Cunkebao/src/components/FriendSelection/TwoColumnSelection.tsx
+++ b/Cunkebao/src/components/FriendSelection/TwoColumnSelection.tsx
@@ -1,10 +1,35 @@
-import React, { useState, useCallback, useEffect } from 'react';
+import React, { useState, useCallback, useEffect, useMemo, memo } from 'react';
import { Modal, Input, Avatar, Button, Checkbox, message } from 'antd';
import { SearchOutlined } from '@ant-design/icons';
import { getFriendList } from './api';
import type { FriendSelectionItem } from './data';
import styles from './TwoColumnSelection.module.scss';
+// 使用 React.memo 优化好友列表项组件
+const FriendListItem = memo<{
+ friend: FriendSelectionItem;
+ isSelected: boolean;
+ onSelect: (friend: FriendSelectionItem) => void;
+}>(({ friend, isSelected, onSelect }) => {
+ return (
+
onSelect(friend)}
+ >
+
+
+ {friend.nickname?.charAt(0)}
+
+
+
{friend.nickname}
+
{friend.wechatId}
+
+
+ );
+});
+
+FriendListItem.displayName = 'FriendListItem';
+
interface TwoColumnSelectionProps {
visible: boolean;
onCancel: () => void;
@@ -12,6 +37,7 @@ interface TwoColumnSelectionProps {
title?: string;
deviceIds?: number[];
enableDeviceFilter?: boolean;
+ dataSource?: FriendSelectionItem[];
}
const TwoColumnSelection: React.FC = ({
@@ -21,14 +47,50 @@ const TwoColumnSelection: React.FC = ({
title = '选择好友',
deviceIds = [],
enableDeviceFilter = true,
+ dataSource,
}) => {
- const [friends, setFriends] = useState([]);
+ const [rawFriends, setRawFriends] = useState([]);
const [selectedFriends, setSelectedFriends] = useState([]);
const [searchQuery, setSearchQuery] = useState('');
const [loading, setLoading] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const [totalPages, setTotalPages] = useState(1);
+ // 使用 useMemo 缓存过滤结果,避免每次渲染都重新计算
+ const filteredFriends = useMemo(() => {
+ const sourceData = dataSource || rawFriends;
+ if (!searchQuery.trim()) {
+ return sourceData;
+ }
+
+ const query = searchQuery.toLowerCase();
+ return sourceData.filter(item =>
+ item.name?.toLowerCase().includes(query) ||
+ item.nickname?.toLowerCase().includes(query)
+ );
+ }, [dataSource, rawFriends, searchQuery]);
+
+ // 分页显示好友列表,避免一次性渲染太多项目
+ const ITEMS_PER_PAGE = 50;
+ const [displayPage, setDisplayPage] = useState(1);
+
+ const friends = useMemo(() => {
+ const startIndex = 0;
+ const endIndex = displayPage * ITEMS_PER_PAGE;
+ return filteredFriends.slice(startIndex, endIndex);
+ }, [filteredFriends, displayPage]);
+
+ const hasMoreFriends = filteredFriends.length > friends.length;
+
+ // 使用 useMemo 缓存选中状态映射,避免每次渲染都重新计算
+ const selectedFriendsMap = useMemo(() => {
+ const map = new Map();
+ selectedFriends.forEach(friend => {
+ map.set(friend.id, true);
+ });
+ return map;
+ }, [selectedFriends]);
+
// 获取好友列表
const fetchFriends = useCallback(async (page: number, keyword: string = '') => {
setLoading(true);
@@ -37,21 +99,22 @@ const TwoColumnSelection: React.FC = ({
page,
pageSize: 20,
};
-
+
if (keyword) {
params.keyword = keyword;
}
-
+
if (enableDeviceFilter && deviceIds.length > 0) {
params.deviceIds = deviceIds;
}
-
+
const response = await getFriendList(params);
-
+
if (response.success) {
- setFriends(response.data.list || []);
+ setRawFriends(response.data.list || []);
setTotalPages(Math.ceil((response.data.total || 0) / 20));
} else {
+ setRawFriends([]);
message.error(response.message || '获取好友列表失败');
}
} catch (error) {
@@ -62,57 +125,100 @@ const TwoColumnSelection: React.FC = ({
}
}, [deviceIds, enableDeviceFilter]);
- // 初始化和搜索
+ // 初始化数据加载
useEffect(() => {
- if (visible) {
- fetchFriends(1, searchQuery);
+ if (visible && !dataSource) {
+ // 只有在没有外部数据源时才调用 API
+ fetchFriends(1);
setCurrentPage(1);
}
- }, [visible, searchQuery, fetchFriends]);
+ }, [visible, dataSource, fetchFriends]);
- // 处理搜索
- const handleSearch = (value: string) => {
- setSearchQuery(value);
- };
-
- // 选择好友
- const handleSelectFriend = (friend: FriendSelectionItem) => {
- const isSelected = selectedFriends.some(f => f.id === friend.id);
- if (isSelected) {
- setSelectedFriends(selectedFriends.filter(f => f.id !== friend.id));
- } else {
- setSelectedFriends([...selectedFriends, friend]);
+ // 重置搜索状态
+ useEffect(() => {
+ if (visible) {
+ setSearchQuery('');
+ setSelectedFriends([]);
+ setLoading(false);
}
- };
+ }, [visible]);
- // 移除已选好友
- const handleRemoveFriend = (friendId: number) => {
- setSelectedFriends(selectedFriends.filter(f => f.id !== friendId));
- };
+ // 防抖搜索处理
+ const handleSearch = useCallback(() => {
+ let timeoutId: NodeJS.Timeout;
+ return (value: string) => {
+ clearTimeout(timeoutId);
+ timeoutId = setTimeout(() => {
+ setDisplayPage(1); // 重置分页
+ if (!dataSource) {
+ fetchFriends(1, value);
+ }
+ }, 300);
+ };
+ }, [dataSource, fetchFriends])();
- // 确认选择
- const handleConfirmSelection = () => {
+ // API搜索处理(当没有外部数据源时)
+ const handleApiSearch = useCallback(async (keyword: string) => {
+ if (!dataSource) {
+ await fetchFriends(1, keyword);
+ }
+ }, [dataSource, fetchFriends]);
+
+ // 加载更多好友
+ const handleLoadMore = useCallback(() => {
+ setDisplayPage(prev => prev + 1);
+ }, []);
+
+ // 防抖搜索
+ useEffect(() => {
+ if (!dataSource && searchQuery.trim()) {
+ const timer = setTimeout(() => {
+ handleApiSearch(searchQuery);
+ }, 300);
+ return () => clearTimeout(timer);
+ }
+ }, [searchQuery, dataSource, handleApiSearch]);
+
+ // 选择好友 - 使用 useCallback 优化性能
+ const handleSelectFriend = useCallback((friend: FriendSelectionItem) => {
+ setSelectedFriends(prev => {
+ const isSelected = prev.some(f => f.id === friend.id);
+ if (isSelected) {
+ return prev.filter(f => f.id !== friend.id);
+ } else {
+ return [...prev, friend];
+ }
+ });
+ }, []);
+
+ // 移除已选好友 - 使用 useCallback 优化性能
+ const handleRemoveFriend = useCallback((friendId: number) => {
+ setSelectedFriends(prev => prev.filter(f => f.id !== friendId));
+ }, []);
+
+ // 确认选择 - 使用 useCallback 优化性能
+ const handleConfirmSelection = useCallback(() => {
const selectedIds = selectedFriends.map(f => f.id.toString());
onConfirm(selectedIds, selectedFriends);
setSelectedFriends([]);
setSearchQuery('');
- };
+ }, [selectedFriends, onConfirm]);
- // 取消选择
- const handleCancelSelection = () => {
+ // 取消选择 - 使用 useCallback 优化性能
+ const handleCancel = useCallback(() => {
setSelectedFriends([]);
setSearchQuery('');
onCancel();
- };
+ }, [onCancel]);
return (
+ ,