From d878a1fcaa4ac0c03812ded8a77c53c04e05bd9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E7=BA=A7=E8=80=81=E7=99=BD=E5=85=94?= Date: Thu, 28 Aug 2025 18:24:05 +0800 Subject: [PATCH] =?UTF-8?q?feat(WechatFriends):=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E8=81=94=E7=B3=BB=E4=BA=BA=E5=88=97=E8=A1=A8=E5=88=86=E9=A1=B5?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加react-window依赖并实现分组联系人列表的分页加载功能,优化大数据量下的性能表现 - 每组初始加载20条数据,滚动到底部可点击加载更多 - 添加加载状态提示和"没有更多了"的结束提示 - 优化样式添加加载更多按钮的容器样式 --- Cunkebao/dist/.vite/manifest.json | 18 +-- Cunkebao/dist/index.html | 8 +- Cunkebao/package.json | 1 + Cunkebao/pnpm-lock.yaml | 22 +++ .../WechatFriends/WechatFriends.module.scss | 17 ++- .../SidebarMenu/WechatFriends/index.tsx | 142 +++++++++++++++--- .../pc/ckbox/components/Skeleton/index.tsx | 54 +++++-- 7 files changed, 212 insertions(+), 50 deletions(-) diff --git a/Cunkebao/dist/.vite/manifest.json b/Cunkebao/dist/.vite/manifest.json index 17e97181..aa2b3d0b 100644 --- a/Cunkebao/dist/.vite/manifest.json +++ b/Cunkebao/dist/.vite/manifest.json @@ -1,9 +1,9 @@ { - "_charts-M0qaf_ew.js": { - "file": "assets/charts-M0qaf_ew.js", + "_charts-DKSCc2_C.js": { + "file": "assets/charts-DKSCc2_C.js", "name": "charts", "imports": [ - "_ui-D5qYGnLz.js", + "_ui-DhAz00L0.js", "_vendor-2vc8h_ct.js" ] }, @@ -11,8 +11,8 @@ "file": "assets/ui-D0C0OGrH.css", "src": "_ui-D0C0OGrH.css" }, - "_ui-D5qYGnLz.js": { - "file": "assets/ui-D5qYGnLz.js", + "_ui-DhAz00L0.js": { + "file": "assets/ui-DhAz00L0.js", "name": "ui", "imports": [ "_vendor-2vc8h_ct.js" @@ -33,18 +33,18 @@ "name": "vendor" }, "index.html": { - "file": "assets/index-BQxyt58_.js", + "file": "assets/index-BdCPAYQ7.js", "name": "index", "src": "index.html", "isEntry": true, "imports": [ "_vendor-2vc8h_ct.js", "_utils-6WF66_dS.js", - "_ui-D5qYGnLz.js", - "_charts-M0qaf_ew.js" + "_ui-DhAz00L0.js", + "_charts-DKSCc2_C.js" ], "css": [ - "assets/index-B6B8u-1D.css" + "assets/index-ChiFk16x.css" ] } } \ No newline at end of file diff --git a/Cunkebao/dist/index.html b/Cunkebao/dist/index.html index dc0fa190..0b6673bb 100644 --- a/Cunkebao/dist/index.html +++ b/Cunkebao/dist/index.html @@ -11,13 +11,13 @@ - + - - + + - +
diff --git a/Cunkebao/package.json b/Cunkebao/package.json index c1b51a74..0c8101e8 100644 --- a/Cunkebao/package.json +++ b/Cunkebao/package.json @@ -15,6 +15,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.20.0", + "react-window": "^1.8.11", "vconsole": "^3.15.1", "zustand": "^5.0.6" }, diff --git a/Cunkebao/pnpm-lock.yaml b/Cunkebao/pnpm-lock.yaml index 11fcddaf..781b87a9 100644 --- a/Cunkebao/pnpm-lock.yaml +++ b/Cunkebao/pnpm-lock.yaml @@ -41,6 +41,9 @@ importers: react-router-dom: specifier: ^6.20.0 version: 6.30.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-window: + specifier: ^1.8.11 + version: 1.8.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1) vconsole: specifier: ^3.15.1 version: 3.15.1 @@ -1563,6 +1566,9 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + memoize-one@5.2.1: + resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -2001,6 +2007,13 @@ packages: peerDependencies: react: '>=16.8' + react-window@1.8.11: + resolution: {integrity: sha512-+SRbUVT2scadgFSWx+R1P754xHPEqvcfSfVX10QYg6POOz+WNgkN48pS+BtZNIMGiL1HYrSEiCkwsMS15QogEQ==} + engines: {node: '>8.0.0'} + peerDependencies: + react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react@18.3.1: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} @@ -4025,6 +4038,8 @@ snapshots: math-intrinsics@1.1.0: {} + memoize-one@5.2.1: {} + merge2@1.4.1: {} micromatch@4.0.8: @@ -4538,6 +4553,13 @@ snapshots: '@remix-run/router': 1.23.0 react: 18.3.1 + react-window@1.8.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + memoize-one: 5.2.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react@18.3.1: dependencies: loose-envify: 1.4.0 diff --git a/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/WechatFriends/WechatFriends.module.scss b/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/WechatFriends/WechatFriends.module.scss index a89c0069..8b371fa2 100644 --- a/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/WechatFriends/WechatFriends.module.scss +++ b/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/WechatFriends/WechatFriends.module.scss @@ -44,8 +44,21 @@ } .groupPanel { - background-color: transparent; - } + background-color: transparent; +} + +.loadMoreContainer { + display: flex; + justify-content: center; + padding: 10px 0; +} + +.noMoreText { + text-align: center; + color: #999; + font-size: 12px; + padding: 10px 0; +} .list { flex: 1; diff --git a/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/WechatFriends/index.tsx b/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/WechatFriends/index.tsx index f177d965..0f9dc529 100644 --- a/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/WechatFriends/index.tsx +++ b/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/WechatFriends/index.tsx @@ -1,5 +1,5 @@ -import React, { useState } from "react"; -import { List, Avatar, Collapse } from "antd"; +import React, { useState, useCallback, useEffect } from "react"; +import { List, Avatar, Collapse, Button } from "antd"; import { ContractData } from "@/pages/pc/ckbox/data"; import styles from "./WechatFriends.module.scss"; import { useCkChatStore } from "@/store/module/ckchat"; @@ -20,6 +20,14 @@ const ContactListSimple: React.FC = ({ const newContractList = useCkChatStore(state => state.newContractList); const [activeKey, setActiveKey] = useState(["0"]); // 默认展开第一个分组 + // 分页加载相关状态 + const [visibleContacts, setVisibleContacts] = useState<{ + [key: string]: ContractData[]; + }>({}); + const [loading, setLoading] = useState<{ [key: string]: boolean }>({}); + const [hasMore, setHasMore] = useState<{ [key: string]: boolean }>({}); + const [page, setPage] = useState<{ [key: string]: number }>({}); + // 渲染联系人项 const renderContactItem = (contact: ContractData) => ( = ({ ); + // 初始化分页数据 + useEffect(() => { + if (newContractList && newContractList.length > 0) { + const initialVisibleContacts: { [key: string]: ContractData[] } = {}; + const initialLoading: { [key: string]: boolean } = {}; + const initialHasMore: { [key: string]: boolean } = {}; + const initialPage: { [key: string]: number } = {}; + + newContractList.forEach((group, index) => { + const groupKey = index.toString(); + // 每个分组初始加载20条数据 + const pageSize = 20; + initialVisibleContacts[groupKey] = group.contacts.slice(0, pageSize); + initialLoading[groupKey] = false; + initialHasMore[groupKey] = group.contacts.length > pageSize; + initialPage[groupKey] = 1; + }); + + setVisibleContacts(initialVisibleContacts); + setLoading(initialLoading); + setHasMore(initialHasMore); + setPage(initialPage); + } + }, [newContractList]); + + // 加载更多联系人 + const loadMoreContacts = useCallback( + (groupKey: string) => { + if (loading[groupKey] || !hasMore[groupKey] || !newContractList) return; + + setLoading(prev => ({ ...prev, [groupKey]: true })); + + // 模拟异步加载 + setTimeout(() => { + const groupIndex = parseInt(groupKey); + const group = newContractList[groupIndex]; + if (!group) return; + + const pageSize = 20; + const currentPage = page[groupKey] || 1; + const nextPage = currentPage + 1; + const startIndex = currentPage * pageSize; + const endIndex = nextPage * pageSize; + const newContacts = group.contacts.slice(startIndex, endIndex); + + setVisibleContacts(prev => ({ + ...prev, + [groupKey]: [...(prev[groupKey] || []), ...newContacts], + })); + + setPage(prev => ({ ...prev, [groupKey]: nextPage })); + setHasMore(prev => ({ + ...prev, + [groupKey]: endIndex < group.contacts.length, + })); + + setLoading(prev => ({ ...prev, [groupKey]: false })); + }, 300); + }, + [loading, hasMore, page, newContractList], + ); + + // 渲染加载更多按钮 + const renderLoadMoreButton = (groupKey: string) => { + if (!hasMore[groupKey]) + return
没有更多了
; + + return ( +
+ +
+ ); + }; + return (
{newContractList && newContractList.length > 0 ? ( @@ -50,26 +138,36 @@ const ContactListSimple: React.FC = ({ activeKey={activeKey} onChange={keys => setActiveKey(keys as string[])} > - {newContractList.map((group, index) => ( - - {group.groupName} - - {group.contacts.length} - -
- } - key={index.toString()} - className={styles.groupPanel} - > - - - ))} + {newContractList.map((group, index) => { + const groupKey = index.toString(); + const isActive = activeKey.includes(groupKey); + + return ( + + {group.groupName} + + {group.contacts.length} + + + } + key={groupKey} + className={styles.groupPanel} + > + {isActive && ( + <> + + {renderLoadMoreButton(groupKey)} + + )} + + ); + })} ) : ( <> diff --git a/Cunkebao/src/pages/pc/ckbox/components/Skeleton/index.tsx b/Cunkebao/src/pages/pc/ckbox/components/Skeleton/index.tsx index f4ea1f1d..128f7414 100644 --- a/Cunkebao/src/pages/pc/ckbox/components/Skeleton/index.tsx +++ b/Cunkebao/src/pages/pc/ckbox/components/Skeleton/index.tsx @@ -1,7 +1,7 @@ -import React from 'react'; -import { Skeleton, Layout } from 'antd'; -import styles from './index.module.scss'; -import pageStyles from '../../index.module.scss'; +import React from "react"; +import { Skeleton, Layout } from "antd"; +import styles from "./index.module.scss"; +import pageStyles from "../../index.module.scss"; const { Header, Content, Sider } = Layout; @@ -29,7 +29,10 @@ const PageSkeleton: React.FC = ({ loading, children }) => { {Array(5) .fill(null) .map((_, index) => ( -
+
))} @@ -42,18 +45,39 @@ const PageSkeleton: React.FC = ({ loading, children }) => {
- - + +
{Array(8) .fill(null) .map((_, index) => ( -
+
- - + +
))} @@ -64,7 +88,7 @@ const PageSkeleton: React.FC = ({ loading, children }) => {
- +
{Array(5) @@ -75,7 +99,11 @@ const PageSkeleton: React.FC = ({ loading, children }) => { className={`${styles.messageSkeleton} ${index % 2 === 0 ? styles.leftMessage : styles.rightMessage}`} > - +
))}
@@ -88,4 +116,4 @@ const PageSkeleton: React.FC = ({ loading, children }) => { ); }; -export default PageSkeleton; \ No newline at end of file +export default PageSkeleton;