Revert ecommerce catalog rematch

This commit is contained in:
Jacky Ser
2026-08-11 15:37:23 +08:00
parent b4d11e2f12
commit 6c37aa3d47
561 changed files with 7342 additions and 16435 deletions

View File

@@ -3,9 +3,15 @@
import { FormEvent, useMemo, useState } from "react";
import {
CATALOG_COUNTS,
CATALOG_PRODUCTS,
CATALOG_PRODUCTS as RAW_CATALOG_PRODUCTS,
type CatalogProduct,
} from "./catalog-data";
import { getCatalogProductImage } from "./catalog-image-map";
const CATALOG_PRODUCTS = RAW_CATALOG_PRODUCTS.map((product) => ({
...product,
image: getCatalogProductImage(product),
}));
type Requirements = {
application: string;
@@ -34,10 +40,12 @@ const APPLICATIONS = [
const PRIMARY_CATALOG_CATEGORIES = [
"标准舵机",
"异形舵机",
"全金属舵机",
"大扭矩舵机",
"积木舵机",
"舵盘配件",
"标准齿轮箱",
"精密齿轮配件",
"翻盖电机",
];
const CATALOG_FILTER_CATEGORIES = ["全部产品", ...PRIMARY_CATALOG_CATEGORIES, "静音舵机"];
@@ -52,13 +60,6 @@ const FEATURED_SPEC_ORDER = [
"寿命",
"产品尺寸",
"外形尺寸",
"空载速度",
"材质",
"模数",
"齿数",
"孔径",
"重量",
"参考单价",
];
function getFeaturedSpecs(product: CatalogProduct) {
@@ -68,32 +69,20 @@ function getFeaturedSpecs(product: CatalogProduct) {
.map((label) => [label, product.specs[label]] as const);
}
const SERVO_CATEGORIES = ["标准舵机", "积木舵机"];
const SERVO_CATEGORIES = ["标准舵机", "异形舵机", "全金属舵机", "大扭矩舵机", "积木舵机"];
const SILENT_SERVO_CATEGORY = "静音舵机";
const CUSTOM_SERVO_CATEGORY = "非标准舵机定制";
const TORQUE_MIN = 0.09;
const TORQUE_MAX = Math.max(
TORQUE_MIN,
...CATALOG_PRODUCTS.filter((product) => SERVO_CATEGORIES.includes(product.category))
.map((product) => product.torqueValue ?? 0),
);
const TORQUE_PRESETS = [...new Set([
0.1,
1,
5,
20,
100,
300,
TORQUE_MAX,
].filter((value) => value <= TORQUE_MAX))];
const TORQUE_MAX = 920;
const TORQUE_PRESETS = [0.1, 1, 5, 20, 100, 300, 920];
const APPLICATION_CATEGORY_MAP: Record<string, string[]> = {
AI毛绒: ["标准舵机"],
: ["标准舵机", "积木舵机"],
AI毛绒: ["标准舵机", "异形舵机"],
: ["标准舵机", "异形舵机", "积木舵机"],
"教育/积木": ["积木舵机", "标准舵机"],
: ["标准舵机", "积木舵机"],
"云台/航模": ["标准舵机"],
: ["标准舵机", "积木舵机"],
: ["标准舵机", "异形舵机", "全金属舵机"],
"云台/航模": ["标准舵机", "全金属舵机"],
: ["全金属舵机", "大扭矩舵机", "异形舵机"],
};
function parseNumbers(value = "") {
@@ -108,7 +97,7 @@ function isSilentServo(product: CatalogProduct) {
const SILENT_SERVO_COUNT = CATALOG_PRODUCTS.filter(isSilentServo).length;
function getCategoryCount(category: string) {
return category === SILENT_SERVO_CATEGORY ? SILENT_SERVO_COUNT : CATALOG_COUNTS[category] ?? 0;
return category === SILENT_SERVO_CATEGORY ? SILENT_SERVO_COUNT : CATALOG_COUNTS[category];
}
type ProductScale = {
@@ -225,10 +214,10 @@ const SELECTOR_PRODUCTS = CATALOG_PRODUCTS.filter((product) =>
weight: inferWeight(product),
isSilent: isSilentServo(product),
gear: classifyGear(product.specs["齿轮材质"]),
angle: product.specs["可操作角度"] ?? "",
angle: product.specs["可操作角度"] ?? product.specs["操作角度范围"] ?? "",
control,
speed: product.specs["空载速度"] ?? "",
size: product.specs["产品尺寸"] ?? "",
speed: product.specs["空载速度"] ?? product.specs["转动速度"] ?? "",
size: product.specs["产品尺寸"] ?? product.specs["外形尺寸"] ?? "",
};
});
@@ -360,8 +349,6 @@ export default function Home() {
product.name,
product.code,
product.category,
product.subCategory,
product.sourceCategory,
...Object.keys(product.specs),
...Object.values(product.specs),
]
@@ -442,8 +429,8 @@ export default function Home() {
<a href="#products" className="text-action"></a>
</div>
<div className="trust-row">
<span><b>{CATALOG_PRODUCTS.length}</b> </span>
<span><b>{formatTorque(TORQUE_MIN)}{formatTorque(TORQUE_MAX)}</b> kgf·cm</span>
<span><b>280</b> </span>
<span><b>0.09920</b> kgf·cm</span>
<span><b>48h</b> </span>
</div>
</div>
@@ -505,7 +492,7 @@ export default function Home() {
<div className="question-step">
<p className="step-kicker"> · </p>
<h2></h2>
<p className="step-help">{SELECTOR_PRODUCTS.length}{TORQUE_MIN}{TORQUE_MAX} kgf·cm</p>
<p className="step-help">2430.09920 kgf·cm</p>
<div className="two-fields compact-fields">
<label className="select-field">
<span></span>
@@ -513,7 +500,7 @@ export default function Home() {
value={requirements.category}
onChange={(event) => update("category", event.target.value)}
>
<option value="全部舵机">{SELECTOR_PRODUCTS.length}</option>
<option value="全部舵机">243</option>
<optgroup label="官方产品系列">
{SERVO_CATEGORIES.map((category) => (
<option key={category} value={category}>
@@ -573,11 +560,7 @@ export default function Home() {
value={torqueToSlider(requirements.torque)}
onChange={(event) => update("torque", sliderToTorque(Number(event.target.value)))}
/>
<div className="range-scale">
{[TORQUE_MIN, 1, 10, 100, TORQUE_MAX]
.filter((value, index, values) => value <= TORQUE_MAX && values.indexOf(value) === index)
.map((value) => <span key={value}>{formatTorque(value)}</span>)}
</div>
<div className="range-scale"><span>0.09</span><span>1</span><span>10</span><span>100</span><span>920</span></div>
<div className="torque-presets" aria-label="常用扭矩快捷值">
{TORQUE_PRESETS.map((torque) => (
<button
@@ -837,7 +820,7 @@ export default function Home() {
{match.reasons.slice(0, 2).map((reason) => <li key={reason}> {reason}</li>)}
</ul>
<div className="source-line">
<span></span>
<span></span>
<strong>{match.product.category}</strong>
<small></small>
</div>
@@ -908,9 +891,9 @@ export default function Home() {
<div className="section-heading">
<div>
<p className="eyebrow"><span /> · 2026.06</p>
<h2>{CATALOG_PRODUCTS.length}</h2>
<h2>280</h2>
</div>
<p></p>
<p>280</p>
</div>
<div className="catalog-summary" aria-label="产品分类统计">
@@ -929,7 +912,7 @@ export default function Home() {
</button>
))}
</div>
<p className="catalog-category-note"> {CATALOG_PRODUCTS.length} 齿齿</p>
<p className="catalog-category-note"> · 280 · 11</p>
<div className="catalog-toolbar">
<label className="catalog-search">
@@ -1006,7 +989,7 @@ export default function Home() {
) : (
<span className="catalog-placeholder">GAUDELOT</span>
)}
<small>{product.subCategory || product.category}</small>
<small>{product.category}</small>
<span className="catalog-zoom-hint"></span>
</button>
<div className="catalog-card-body">
@@ -1049,8 +1032,6 @@ export default function Home() {
role="presentation"
onMouseDown={() => setSelectedCatalogProduct(null)}
>
{/* Stop a click inside the dialog from closing the backdrop. */}
{/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */}
<section
className="catalog-modal"
role="dialog"
@@ -1076,7 +1057,7 @@ export default function Home() {
<p className="step-kicker">{selectedCatalogProduct.category}</p>
<h2 id="catalog-modal-title">{selectedCatalogProduct.name}</h2>
<code>{selectedCatalogProduct.code}</code>
<p className="catalog-source-note"> {selectedCatalogProduct.sourceId} · {selectedCatalogProduct.sourceCategory}</p>
<p className="catalog-source-note"> {selectedCatalogProduct.page} </p>
</div>
</div>
<div className="catalog-spec-table">