如何用SPFx+React+PnP SP实现多SharePoint列表关联数据展示WebPart
解决SPFx中多列表数据关联与层级渲染的方案
一、并行获取三个列表的数据
用PnP SP的Promise.all同时请求三个列表的items,避免串行请求拖慢速度,同时指定需要的字段减少数据传输量:
import { sp } from "@pnp/sp/presets/all"; // 定义数据类型(增强类型提示,可选) interface Category { Id: number; Title: string; } interface SubCategory { Id: number; Title: string; CategoryId: number; // 关联分类列表的Id字段 } interface Project { Id: number; Title: string; CategoryId: number; SubCategoryId: number; // 关联子分类列表的Id字段 // 可添加其他项目字段 } // 获取所有数据的异步函数 const fetchAllData = async (): Promise<{categories: Category[], subCategories: SubCategory[], projects: Project[]}> => { try { // 并行发起三个列表的请求 const [categories, subCategories, projects] = await Promise.all([ sp.web.lists.getByTitle("分类列表").items.select("Id", "Title").get<Category[]>(), sp.web.lists.getByTitle("子分类列表").items.select("Id", "Title", "CategoryId").get<SubCategory[]>(), sp.web.lists.getByTitle("项目列表").items.select("Id", "Title", "CategoryId", "SubCategoryId").get<Project[]>(), ]); return { categories, subCategories, projects }; } catch (error) { console.error("获取数据失败:", error); throw error; } };
二、构建分类→子分类→项目的层级结构
先把数据转换成便于查询的分组对象,再组装成目标层级:
const buildHierarchy = (categories: Category[], subCategories: SubCategory[], projects: Project[]) => { // 按分类ID分组子分类 const subCatsByCategory = subCategories.reduce((acc, subCat) => { const catId = subCat.CategoryId; if (!acc[catId]) acc[catId] = []; acc[catId].push(subCat); return acc; }, {} as Record<number, SubCategory[]>); // 按子分类ID分组项目 const projectsBySubCat = projects.reduce((acc, project) => { const subCatId = project.SubCategoryId; if (!acc[subCatId]) acc[subCatId] = []; acc[subCatId].push(project); return acc; }, {} as Record<number, Project[]>); // 组装最终层级数据 return categories.map(category => ({ ...category, subCategories: subCatsByCategory[category.Id]?.map(subCat => ({ ...subCat, projects: projectsBySubCat[subCat.Id] || [] })) || [] })); };
三、React组件中渲染层级结构
在组件的useEffect中加载数据并构建层级,用CSS Grid实现子分类的3列布局:
import { useEffect, useState } from "react"; import styles from "./YourWebPart.module.scss"; const CategoryHierarchy = () => { const [hierarchy, setHierarchy] = useState<(Category & {subCategories: (SubCategory & {projects: Project[]})[]})[]>([]); useEffect(() => { const loadData = async () => { const { categories, subCategories, projects } = await fetchAllData(); const builtHierarchy = buildHierarchy(categories, subCategories, projects); setHierarchy(builtHierarchy); }; loadData(); }, []); return ( <div className={styles.container}> {hierarchy.map(category => ( <div key={category.Id} className={styles.categorySection}> <h2 className={styles.categoryTitle}>{category.Title}</h2> {/* 3列网格布局展示子分类 */} <div className={styles.subCategoriesGrid}> {category.subCategories.map(subCat => ( <div key={subCat.Id} className={styles.subCategoryCard}> <h3 className={styles.subCategoryTitle}>{subCat.Title}</h3> <ul className={styles.projectList}> {subCat.projects.map(project => ( <li key={project.Id} className={styles.projectItem}>{project.Title}</li> ))} </ul> </div> ))} </div> </div> ))} </div> ); }; export default CategoryHierarchy;
四、SCSS样式示例(实现3列布局)
在对应的.module.scss文件中添加布局样式:
.container { padding: 1rem; } .categorySection { margin-bottom: 2rem; } .categoryTitle { font-size: 1.5rem; margin-bottom: 1rem; border-bottom: 2px solid #eee; padding-bottom: 0.5rem; } .subCategoriesGrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; } .subCategoryCard { padding: 1rem; border: 1px solid #eee; border-radius: 4px; background-color: #f9f9f9; } .subCategoryTitle { font-size: 1.2rem; margin-bottom: 0.8rem; } .projectList { list-style-type: disc; padding-left: 1.5rem; } .projectItem { margin-bottom: 0.3rem; }
注意事项
- 确保列表中的关联字段(如子分类的
CategoryId、项目的SubCategoryId)是正确的数字类型,避免关联失败。 - 若列表数据量较大,建议添加分页或筛选条件,避免一次性加载过多数据影响性能。
- 可根据实际需求调整字段名称、样式及展示内容。
内容的提问来源于stack exchange,提问作者Hasnain Ali
相关产品推荐
相关产品推荐

