Add g and kg catalog filters
This commit is contained in:
73
app/page.tsx
73
app/page.tsx
@@ -94,6 +94,46 @@ function getCategoryCount(category: string) {
|
||||
return category === SILENT_SERVO_CATEGORY ? SILENT_SERVO_COUNT : CATALOG_COUNTS[category];
|
||||
}
|
||||
|
||||
type ProductScale = {
|
||||
key: string;
|
||||
label: string;
|
||||
unit: "g" | "kg";
|
||||
value: number;
|
||||
};
|
||||
|
||||
function getProductScale(product: CatalogProduct): ProductScale | null {
|
||||
const kilogramMatch = product.name.match(/(\d+(?:\.\d+)?)\s*(?:kg|千克)/i);
|
||||
if (kilogramMatch) {
|
||||
const value = Number(kilogramMatch[1]);
|
||||
return { key: `kg:${value}`, label: `${value}kg`, unit: "kg", value };
|
||||
}
|
||||
|
||||
const gramMatch = product.name.match(/(\d+(?:\.\d+)?)\s*(?:g|克)/i);
|
||||
if (gramMatch) {
|
||||
const value = Number(gramMatch[1]);
|
||||
return { key: `g:${value}`, label: `${value}g`, unit: "g", value };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const PRODUCT_SCALE_OPTIONS = [...CATALOG_PRODUCTS.reduce((options, product) => {
|
||||
const scale = getProductScale(product);
|
||||
if (!scale) return options;
|
||||
const current = options.get(scale.key);
|
||||
options.set(scale.key, { ...scale, count: (current?.count ?? 0) + 1 });
|
||||
return options;
|
||||
}, new Map<string, ProductScale & { count: number }>()).values()].sort(
|
||||
(a, b) => (a.unit === b.unit ? a.value - b.value : a.unit === "g" ? -1 : 1),
|
||||
);
|
||||
|
||||
const GRAM_SCALE_OPTIONS = PRODUCT_SCALE_OPTIONS.filter((option) => option.unit === "g");
|
||||
const KILOGRAM_SCALE_OPTIONS = PRODUCT_SCALE_OPTIONS.filter((option) => option.unit === "kg");
|
||||
const UNMARKED_SCALE_COUNT = CATALOG_PRODUCTS.length - PRODUCT_SCALE_OPTIONS.reduce(
|
||||
(total, option) => total + option.count,
|
||||
0,
|
||||
);
|
||||
|
||||
function inferWeight(product: CatalogProduct) {
|
||||
const listedWeight = parseNumbers(product.specs["重量"])[0];
|
||||
if (Number.isFinite(listedWeight)) return listedWeight;
|
||||
@@ -280,6 +320,7 @@ export default function Home() {
|
||||
const [leadId, setLeadId] = useState("");
|
||||
const [catalogQuery, setCatalogQuery] = useState("");
|
||||
const [catalogCategory, setCatalogCategory] = useState("全部产品");
|
||||
const [catalogScale, setCatalogScale] = useState("all");
|
||||
const [catalogLimit, setCatalogLimit] = useState(24);
|
||||
const [selectedCatalogProduct, setSelectedCatalogProduct] = useState<CatalogProduct | null>(null);
|
||||
|
||||
@@ -290,6 +331,8 @@ export default function Home() {
|
||||
const filteredCatalog = useMemo(() => {
|
||||
const query = catalogQuery.trim().toLowerCase();
|
||||
return CATALOG_PRODUCTS.filter((product) => {
|
||||
const productScale = getProductScale(product)?.key ?? "unmarked";
|
||||
if (catalogScale !== "all" && productScale !== catalogScale) return false;
|
||||
const matchesCategory =
|
||||
catalogCategory === "全部产品" ||
|
||||
product.category === catalogCategory ||
|
||||
@@ -307,7 +350,7 @@ export default function Home() {
|
||||
.toLowerCase();
|
||||
return haystack.includes(query);
|
||||
});
|
||||
}, [catalogCategory, catalogQuery]);
|
||||
}, [catalogCategory, catalogQuery, catalogScale]);
|
||||
const visibleCatalog = filteredCatalog.slice(0, catalogLimit);
|
||||
const needsEngineer =
|
||||
!top?.coreCompatible ||
|
||||
@@ -894,6 +937,32 @@ export default function Home() {
|
||||
>{category === SILENT_SERVO_CATEGORY ? `${category} · ${SILENT_SERVO_COUNT}` : category}</button>
|
||||
))}
|
||||
</div>
|
||||
<div className="catalog-scale-filter">
|
||||
<label>
|
||||
<span>按 g / kg 型号规格筛选</span>
|
||||
<select
|
||||
value={catalogScale}
|
||||
onChange={(event) => {
|
||||
setCatalogScale(event.target.value);
|
||||
setCatalogLimit(24);
|
||||
}}
|
||||
>
|
||||
<option value="all">全部 g / kg 规格</option>
|
||||
<optgroup label="克重系列(g / 克)">
|
||||
{GRAM_SCALE_OPTIONS.map((option) => (
|
||||
<option key={option.key} value={option.key}>{option.label}({option.count}款)</option>
|
||||
))}
|
||||
</optgroup>
|
||||
<optgroup label="千克扭矩等级(kg / 千克)">
|
||||
{KILOGRAM_SCALE_OPTIONS.map((option) => (
|
||||
<option key={option.key} value={option.key}>{option.label}({option.count}款)</option>
|
||||
))}
|
||||
</optgroup>
|
||||
<option value="unmarked">其他未标注 g/kg({UNMARKED_SCALE_COUNT}款)</option>
|
||||
</select>
|
||||
</label>
|
||||
<p><strong>说明:</strong>g/克表示产品克重系列;kg/Kg通常表示舵机扭矩等级,并非产品自身重量。</p>
|
||||
</div>
|
||||
<p className="catalog-result-count">
|
||||
找到 <strong>{filteredCatalog.length}</strong> 个型号
|
||||
</p>
|
||||
@@ -936,7 +1005,7 @@ export default function Home() {
|
||||
<div className="catalog-empty">
|
||||
<strong>没有找到匹配型号</strong>
|
||||
<p>可以尝试减少关键词,或切换到“全部产品”。</p>
|
||||
<button type="button" onClick={() => { setCatalogQuery(""); setCatalogCategory("全部产品"); }}>
|
||||
<button type="button" onClick={() => { setCatalogQuery(""); setCatalogCategory("全部产品"); setCatalogScale("all"); }}>
|
||||
清除筛选
|
||||
</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user