当前位置: 首页 > news >正文

用自然语言描述电商商品列表(带筛选、分页),生成组件后二次优化

文章目录

  • 一、自然语言描述转化的基础组件代码(React + TypeScript)
  • 二、组件二次优化(性能 + 体验 + 扩展性升级)
    • 优化 1:性能优化(避免重复渲染 + 数据缓存)
    • 优化 2:交互体验升级
    • 优化 3:扩展性升级(支持自定义配置)
  • 优化后组件核心优势

一、自然语言描述转化的基础组件代码(React + TypeScript)

import React, { useState, useEffect } from 'react'; import { Input, Select, Checkbox, Pagination, Card, Spin, Empty } from 'antd'; import { SearchOutlined, FilterOutlined } from '@ant-design/icons'; // 商品类型定义 interface Product { id: number; name: string; price: number; category: string; brand: string; sales: number; image: string; } // 筛选条件类型 interface FilterParams { keyword: string; category: string; brand: string[]; priceRange: [number, number]; } const ProductList: React.FC = () => { // 状态管理 const [products, setProducts] = useState<Product[]>([]); const [filteredProducts, setFilteredProducts] = useState<Product[]>([]); const [filters, setFilters] = useState<FilterParams>({ keyword: '', category: 'all', brand: [], priceRange: [0, 10000], }); const [loading, setLoading] = useState<boolean>(true); const [currentPage, setCurrentPage] = useState<number>(1); const [pageSize] = useState<number>(12); // 模拟接口请求商品数据 useEffect(() => { const fetchProducts = async () => { setLoading(true); // 模拟延迟 await new Promise(resolve => setTimeout(resolve, 800)); // 模拟商品数据 const mockData: Product[] = Array.from({ length: 60 }, (_, i) => ({ id: i + 1, name: `智能防蓝光眼镜 ${i + 1}代 - ${['商务款', '休闲款', '运动款'][i % 3]}`, price: 199 + Math.floor(Math.random() * 800), category: ['防蓝光', '近视镜', '太阳镜', '老花镜'][i % 4], brand: ['暴龙', '陌森', '雷朋', '帕森', '海伦凯勒'][i % 5], sales: Math.floor(Math.random() * 1000), image: `https://picsum.photos/200/200?random=${i}`, })); setProducts(mockData); setFilteredProducts(mockData); setLoading(false); }; fetchProducts(); }, []); // 筛选逻辑 useEffect(() => { let result = [...products]; // 关键词筛选 if (filters.keyword) { result = result.filter(product => product.name.toLowerCase().includes(filters.keyword.toLowerCase()) ); } // 分类筛选 if (filters.category !== 'all') { result = result.filter(product => product.category === filters.category); } // 品牌筛选 if (filters.brand.length > 0) { result = result.filter(product => filters.brand.includes(product.brand)); } // 价格范围筛选 result = result.filter( product => product.price >= filters.priceRange[0] && product.price <= filters.priceRange[1] ); setFilteredProducts(result); setCurrentPage(1); // 筛选后重置到第一页 }, [filters, products]); // 处理筛选条件变更 const handleFilterChange = (key: keyof FilterParams, value: any) => { setFilters(prev => ({ ...prev, [key]: value })); }; // 分页数据计算 const paginatedProducts = filteredProducts.slice( (currentPage - 1) * pageSize, currentPage * pageSize ); // 分类选项(从商品数据中提取唯一值) const categoryOptions = Array.from(new Set(products.map(p => p.category))).map(cat => ({ label: cat, value: cat, })); // 品牌选项(从商品数据中提取唯一值) const brandOptions = Array.from(new Set(products.map(p => p.brand))).map(brand => ({ label: brand, value: brand, })); return ( <div style={{ padding: '20px', maxWidth: '1400px', margin: '0 auto' }}> <h2 style={{ marginBottom: '20px', fontWeight: 600 }}>眼镜商品列表</h2> {/* 筛选区域 */} <div style={{ background: '#f8f9fa', padding: '20px', borderRadius: '8px', marginBottom: '20px', display: 'flex', flexWrap: 'wrap', gap: '16px', alignItems: 'center' }}> <div style={{ flex: 1, minWidth: '200px' }}> <Input placeholder="搜索商品名称..." prefix={<SearchOutlined />} value={filters.keyword} onChange={e => handleFilterChange('keyword', e.target.value)} style={{ width: '100%' }} /> </div> <div style={{ width: '180px' }}> <Select placeholder="选择分类" value={filters.category} onChange={value => handleFilterChange('category', value)} style={{ width: '100%' }} > <Select.Option value="all">全部分类</Select.Option> {categoryOptions} </Select> </div> <div style={{ flex: 1, minWidth: '250px' }}> <div style={{ marginBottom: '8px', fontSize: '14px', color: '#666' }}>选择品牌</div> <div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px' }}> {brandOptions.map(brand => ( <Checkbox key={brand.value} checked={filters.brand.includes(brand.value)} onChange={e => { const newBrands = e.target.checked ? [...filters.brand, brand.value] : filters.brand.filter(b => b !== brand.value); handleFilterChange('brand', newBrands); }} > {brand.label} </Checkbox> ))} </div> </div> <div style={{ width: '220px' }}> <div style={{ marginBottom: '8px', fontSize: '14px', color: '#666' }}>价格范围</div> <div style={{ display: 'flex', gap: '8px' }}> <Input type="number" placeholder="最低" value={filters.priceRange[0] || ''} onChange={e => handleFilterChange('priceRange', [ Number(e.target.value) || 0, filters.priceRange[1] ])} style={{ width: '50%' }} /> <Input type="number" placeholder="最高" value={filters.priceRange[1] || ''} onChange={e => handleFilterChange('priceRange', [ filters.priceRange[0], Number(e.target.value) || 10000 ])} style={{ width: '50%' }} /> </div> </div> <div> <button style={{ background: '#1890ff', color: 'white', border: 'none', borderRadius: '4px', padding: '8px 16px', cursor: 'pointer', display: 'flex', alignItems: 'center', gap: '4px' }} onClick={() => setFilters({ keyword: '', category: 'all', brand: [], priceRange: [0, 10000], })} > <FilterOutlined /> 重置筛选 </button> </div> </div> {/* 商品列表区域 */} <Spin spinning={loading} tip="加载商品中..."> {filteredProducts.length === 0 ? ( <Empty description="暂无符合条件的商品" style={{ padding: '50px 0' }} /> ) : ( <> <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(220px, 1fr))', gap: '20px', marginBottom: '20px' }}> {paginatedProducts.map(product => ( <Card key={product.id} hoverable cover={<img src={product.image} alt={product.name} style={{ height: '200px', objectFit: 'cover' }} />} style={{ borderRadius: '8px', border: '1px solid #e8e8e8' }} > <Card.Meta title={ <div style={{ fontSize: '14px', fontWeight: 500, marginBottom: '8px', display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical', overflow: 'hidden' }}> {product.name} </div> } description={ <div> <span style={{ color: '#ff4d4f', fontSize: '18px', fontWeight: 600 }}> ¥{product.price} </span> <div style={{ fontSize: '12px', color: '#999', marginTop: '4px', display: 'flex', justifyContent: 'space-between' }}> <span>品牌:{product.brand}</span> <span>销量:{product.sales}+</span> </div> </div> } /> </Card> ))} </div> {/* 分页区域 */} <div style={{ display: 'flex', justifyContent: 'center', marginTop: '30px' }}> <Pagination current={currentPage} pageSize={pageSize} total={filteredProducts.length} onChange={setCurrentPage} showSizeChanger={false} showQuickJumper showTotal={(total) => `共 ${total} 件商品`} /> </div> </> )} </Spin> </div> ); }; export default ProductList;

二、组件二次优化(性能 + 体验 + 扩展性升级)

优化 1:性能优化(避免重复渲染 + 数据缓存)

// 1. 引入React.memo和useMemo/useCallback import React, { useState, useEffect, useMemo, useCallback } from 'react'; // 2. 商品卡片组件单独抽离并使用memo缓存 const ProductCard = React.memo(({ product }: { product: Product }) => ( <Card hoverable cover={<img src={product.image} alt={product.name} style={{ height: '200px', objectFit: 'cover' }} />} style={{ borderRadius: '8px', border: '1px solid #e8e8e8', transition: 'transform 0.3s ease' }} onMouseEnter={(e) => e.currentTarget.style.transform = 'translateY(-5px)'} onMouseLeave={(e) => e.currentTarget.style.transform = 'translateY(0)'} > <Card.Meta title={ <div style={{ fontSize: '14px', fontWeight: 500, marginBottom: '8px', display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical', overflow: 'hidden' }}> {product.name} </div> } description={ <div> <span style={{ color: '#ff4d4f', fontSize: '18px', fontWeight: 600 }}> ¥{product.price} </span> <div style={{ fontSize: '12px', color: '#999', marginTop: '4px', display: 'flex', justifyContent: 'space-between' }}> <span>品牌:{product.brand}</span> <span>销量:{product.sales}+</span> </div> </div> } /> </Card> )); // 3. 筛选逻辑用useMemo缓存,依赖变化才重新计算 const filteredProducts = useMemo(() => { return products.filter(product => { const matchKeyword = product.name.toLowerCase().includes(filters.keyword.toLowerCase()); const matchCategory = filters.category === 'all' ? true : product.category === filters.category; const matchBrand = filters.brand.length === 0 ? true : filters.brand.includes(product.brand); const matchPrice = product.price >= filters.priceRange[0] && product.price <= filters.priceRange[1]; return matchKeyword && matchCategory && matchBrand && matchPrice; }); }, [filters, products]); // 4. 事件处理函数用useCallback缓存 const handleFilterChange = useCallback((key: keyof FilterParams, value: any) => { setFilters(prev => ({ ...prev, [key]: value })); }, []); const resetFilters = useCallback(() => { setFilters({ keyword: '', category: 'all', brand: [], priceRange: [0, 10000], }); }, []);

优化 2:交互体验升级

// 1. 价格筛选改为滑块组件(更直观) import { Slider } from 'antd'; // 价格筛选区域替换为: <div style={{ width: '250px' }}> <div style={{ marginBottom: '8px', fontSize: '14px', color: '#666' }}> 价格范围:¥{filters.priceRange[0]} - ¥{filters.priceRange[1]} </div> <Slider range min={0} max={10000} step={10} value={filters.priceRange} onChange={value => handleFilterChange('priceRange', value as [number, number])} style={{ width: '100%' }} /> </div> // 2. 品牌筛选添加"全选/取消全选" <div style={{ flex: 1, minWidth: '250px' }}> <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '8px' }}> <span style={{ fontSize: '14px', color: '#666' }}>选择品牌</span> <Checkbox checked={filters.brand.length === brandOptions.length && brandOptions.length > 0} indeterminate={filters.brand.length > 0 && filters.brand.length < brandOptions.length} onChange={e => { const newBrands = e.target.checked ? brandOptions.map(b => b.value) : []; handleFilterChange('brand', newBrands); }} > 全选 </Checkbox> </div> <div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px' }}> {brandOptions.map(brand => ( <Checkbox key={brand.value} checked={filters.brand.includes(brand.value)} onChange={e => { const newBrands = e.target.checked ? [...filters.brand, brand.value] : filters.brand.filter(b => b !== brand.value); handleFilterChange('brand', newBrands); }} > {brand.label} </Checkbox> ))} </div> </div> // 3. 添加排序功能 const [sortType, setSortType] = useState<'default' | 'priceAsc' | 'priceDesc' | 'salesDesc'>('default'); // 排序后的商品数据 const sortedProducts = useMemo(() => { const copy = [...filteredProducts]; switch (sortType) { case 'priceAsc': return copy.sort((a, b) => a.price - b.price); case 'priceDesc': return copy.sort((a, b) => b.price - a.price); case 'salesDesc': return copy.sort((a, b) => b.sales - a.sales); default: return copy.sort((a, b) => a.id - b.id); } }, [filteredProducts, sortType]); // 排序选择器(添加在筛选区域下方) <div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: '16px' }}> <Select placeholder="选择排序方式" value={sortType} onChange={setSortType} style={{ width: '200px' }} > <Select.Option value="default">默认排序</Select.Option> <Select.Option value="priceAsc">价格从低到高</Select.Option> <Select.Option value="priceDesc">价格从高到低</Select.Option> <Select.Option value="salesDesc">销量从高到低</Select.Option> </Select> </div> // 分页数据改为基于排序后的数据 const paginatedProducts = sortedProducts.slice( (currentPage - 1) * pageSize, currentPage * pageSize );

优化 3:扩展性升级(支持自定义配置)

// 1. 定义组件Props,支持外部传入配置 interface ProductListProps { pageSize?: number; maxPrice?: number; placeholder?: string; onProductClick?: (product: Product) => void; } // 2. 组件接收Props并设置默认值 const ProductList: React.FC<ProductListProps> = ({ pageSize = 12, maxPrice = 10000, placeholder = "搜索商品名称...", onProductClick, }) => { // 3. 价格筛选最大值使用外部传入的maxPrice const [filters, setFilters] = useState<FilterParams>({ keyword: '', category: 'all', brand: [], priceRange: [0, maxPrice], }); // 4. 商品卡片添加点击事件 <ProductCard key={product.id} product={product} onClick={() => onProductClick?.(product)} /> }; // 5. 暴露默认导出 export default ProductList;

优化后组件核心优势

  1. 性能更优:通过memo、useMemo、useCallback减少重复渲染,筛选逻辑惰性计算;
  2. 体验更好:滑块价格筛选、品牌全选、排序功能,操作更直观高效;
  3. 扩展性强:支持自定义分页大小、价格上限、搜索占位符,以及商品点击回调;
  4. 视觉更美观:商品卡片 hover 动画、布局响应式适配,适配不同屏幕尺寸。
http://www.cnnetsun.cn/news/5538.html

相关文章:

  • eventpp终极集成指南:5种快速配置C++事件处理库的方法
  • 项目风险管理 论文框架
  • 30+专业幻灯片模板集:轻松打造精美演示文稿
  • 传统中文手写数据集全面解析与应用指南
  • Host侧算子实现总览-解码Ascend C算子的“CPU端蓝图“
  • 科普多种mfc100u.dll丢失的解决方法!全面了解mfc100u.dll文件
  • Wan2.2-T2V-A14B在新闻摘要视频自动生成中的实验成果
  • 31、互联网用户安全防护全解析
  • 技术日报|Kaiju游戏引擎逆袭夺冠,Claude记忆插件日增779星登榜第三
  • 《2025提示工程从入门到进阶指南》正式发布 | 中科算网算泥社区
  • 换了 4 家 AI 模型,代码只动了 1 行——这个架构设计让老板随便折腾
  • 【毕业设计】基于SpringBoot的网上订餐系统设计与实现(基于java网上订餐系统的设计与实现(源码+文档+远程调试,全bao定制等)
  • Python大佬正在用的,但你不知道的几个编程技巧
  • 5步掌握pywebview与React桌面应用开发:终极跨平台解决方案
  • 如何快速获取BDD100K数据集:计算机视觉训练完整指南
  • 【C语言】分支语句(简略版)
  • IP防水等级分为几个等级
  • 2025年国内网络准入系统排行榜,六款超好用的网络准入系统推荐
  • Statuspage开源状态页面终极部署指南:30分钟搭建专业服务监控平台
  • GoldenDict-ng终极配置指南:打造你的专属词典库
  • 5步轻松掌握MinerU:智能文档转换工具完全指南
  • 自动化测试的「千里眼」:当RTSM远程控制遇上自动化,测试效率直接拉满
  • Spring AI 核心架构总览(资深架构师深度解析)
  • 改进YOLOv8结合跨尺度多头自注意力机制实现野火烟雾检测
  • 致进食障碍者
  • 深度合成算法备案超全解析!从定义到落地的4步合规法则
  • 我如何设计一个不会“发疯”的多智能体系统?
  • day122—二分查找—完成旅途的最少时间(LeetCode-2187)
  • 2025实测7款AI写小说神器!从卡文到日更,新手老手都适配
  • 8、Web漏洞扫描与利用技术详解