You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从零搭建分步筛选式多选项Product Selector的技术问询

从零搭建逐步筛选式产品选择器

1. 先梳理核心逻辑与数据结构

首先得把产品和筛选维度理清楚,参考你需要的层级筛选样式,先明确核心要素:

  • 定义筛选层级:比如步骤1:产品大类→步骤2:产品子类→步骤3:关键参数,层级间要有依赖关系(选了大类才显示对应子类)
  • 产品数据结构示例(JSON格式):
{
  "products": [
    {
      "id": "prod001",
      "name": "工业接近传感器",
      "category": "FA",
      "subCategory": "142",
      "params": {"防护等级": "IP67", "输出信号": "PNP", "检测距离": "10mm"}
    }
  ],
  "filters": [
    {
      "step": 1,
      "name": "产品大类",
      "options": [{"value": "FA", "label": "工厂自动化"}, {"value": "PA", "label": "过程自动化"}]
    },
    {
      "step": 2,
      "name": "产品子类",
      "dependencies": {"FA": ["142", "143"], "PA": ["201", "202"]},
      "options": [{"value": "142", "label": "接近传感器"}, {"value": "143", "label": "光电传感器"}]
    },
    {
      "step": 3,
      "name": "防护等级",
      "dependencies": {"142": ["IP65", "IP67"], "143": ["IP66", "IP68"]},
      "options": [{"value": "IP67", "label": "IP67"}, {"value": "IP65", "label": "IP65"}]
    }
  ]
}

2. 前端实现(以React为例,Vue/原生JS逻辑通用)

核心是根据用户选择动态加载下一级选项+实时过滤产品,代码片段如下:

import { useState, useEffect } from 'react';

const ProductSelector = ({ products, filters }) => {
  const [selected, setSelected] = useState({});
  const [currentStep, setCurrentStep] = useState(1);
  const [matchedProducts, setMatchedProducts] = useState(products);

  // 选中项变化时,过滤产品
  useEffect(() => {
    let result = [...products];
    // 按层级依次过滤
    Object.entries(selected).forEach(([stepKey, value]) => {
      const step = parseInt(stepKey);
      const filterKey = step === 1 ? 'category' : step === 2 ? 'subCategory' : 'params.防护等级';
      result = result.filter(prod => eval(`prod.${filterKey}`) === value);
    });
    setMatchedProducts(result);
  }, [selected, products]);

  // 获取当前步骤的可用选项
  const getCurrentOptions = () => {
    const currentFilter = filters.find(f => f.step === currentStep);
    if (!currentFilter.dependencies) return currentFilter.options;
    // 根据上一步的选择返回对应选项
    const prevValue = selected[currentStep - 1];
    return currentFilter.options.filter(opt => currentFilter.dependencies[prevValue]?.includes(opt.value));
  };

  return (
    <div className="product-selector">
      {/* 步骤导航栏 */}
      <div className="step-nav">
        {filters.map(filter => (
          <button
            key={filter.step}
            className={currentStep === filter.step ? 'active' : selected[filter.step] ? 'completed' : ''}
            onClick={() => setCurrentStep(filter.step)}
            disabled={!selected[filter.step - 1] && filter.step > 1}
          >
            {filter.name}
          </button>
        ))}
      </div>

      {/* 当前步骤选项区 */}
      <div className="option-group">
        {getCurrentOptions().map(opt => (
          <label key={opt.value} className="option-card">
            <input
              type="radio"
              value={opt.value}
              checked={selected[currentStep] === opt.value}
              onChange={(e) => {
                setSelected({...selected, [currentStep]: e.target.value});
                // 非最后一步自动进入下一级
                if (currentStep < filters.length) setCurrentStep(currentStep + 1);
              }}
            />
            <span>{opt.label}</span>
          </label>
        ))}
      </div>

      {/* 产品结果展示 */}
      {currentStep === filters.length && (
        <div className="product-results">
          <h3>匹配产品 ({matchedProducts.length} 个)</h3>
          <div className="product-list">
            {matchedProducts.map(prod => (
              <div key={prod.id} className="product-card">
                <h4>{prod.name}</h4>
                <p>防护等级:{prod.params["防护等级"]}</p>
                <p>检测距离:{prod.params["检测距离"]}</p>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
};

3. 样式优化(贴近参考效果)

示例CSS片段,实现步骤进度感、选项卡片化:

.product-selector .step-nav {
  display: flex;
  gap: 8px;
  margin-bottom: 24px;
}
.step-nav button {
  padding: 10px 20px;
  border: 1px solid #ddd;
  border-radius: 4px;
  background: #f5f5f5;
  cursor: pointer;
}
.step-nav button.active {
  background: #0066cc;
  color: white;
  border-color: #0066cc;
}
.step-nav button.completed {
  background: #e8f4f8;
  border-color: #0066cc;
}
.step-nav button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.option-group {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 16px;
  margin-bottom: 32px;
}
.option-card {
  display: flex;
  align-items: center;
  padding: 16px;
  border: 1px solid #ddd;
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.2s;
}
.option-card:hover {
  border-color: #0066cc;
  background: #f8fafc;
}
.option-card input[type="radio"]:checked + span {
  color: #0066cc;
  font-weight: 600;
}

.product-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 20px;
}
.product-card {
  padding: 16px;
  border: 1px solid #eee;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}

4. 后端支持(数据量大时必做)

如果产品数据量过万,前端全量加载会卡顿,需要后端提供接口:

  • 接口1:GET /api/filters?step=1 获取第一步筛选选项
  • 接口2:GET /api/filters?step=2&prev=FA 根据上一步选择返回对应子类选项
  • 接口3:POST /api/products/filter 传入选中的筛选参数,返回匹配的产品列表
    示例Node.js/Express路由:
app.get('/api/filters', (req, res) => {
  const { step, prev } = req.query;
  // 从数据库查询对应层级的选项
  const options = db.query(`SELECT value, label FROM filters WHERE step = ? ${prev ? 'AND prev_value = ?' : ''}`, [step, prev]);
  res.json(options);
});

app.post('/api/products/filter', (req, res) => {
  const { selected } = req.body;
  // 构建过滤条件查询产品
  const products = db.query(`SELECT * FROM products WHERE category = ? AND sub_category = ? AND protection_level = ?`, [selected[1], selected[2], selected[3]]);
  res.json(products);
});

5. 实用扩展功能

  • 重置筛选:添加按钮清空所有选中项,回到第一步
  • 多选支持:把单选框改成复选框,过滤逻辑改为包含所有选中值
  • URL参数同步:把选中的筛选参数拼到URL(如?step1=FA&step2=142),支持分享筛选结果
  • 搜索补充:在结果区添加产品搜索框,进一步缩小范围

内容的提问来源于stack exchange,提问作者Naveed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 01:45:30