Clarify official and feature categories
This commit is contained in:
@@ -246,7 +246,7 @@ select:focus, input:focus, textarea:focus { border-color: var(--blue); box-shado
|
|||||||
.section-heading > p { max-width: 480px; margin: 0; color: #6e7a8e; line-height: 1.8; font-size: 13px; }
|
.section-heading > p { max-width: 480px; margin: 0; color: #6e7a8e; line-height: 1.8; font-size: 13px; }
|
||||||
.catalog-summary {
|
.catalog-summary {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(8, 1fr);
|
grid-template-columns: repeat(7, 1fr);
|
||||||
gap: 9px;
|
gap: 9px;
|
||||||
margin-bottom: 22px;
|
margin-bottom: 22px;
|
||||||
}
|
}
|
||||||
@@ -266,6 +266,7 @@ select:focus, input:focus, textarea:focus { border-color: var(--blue); box-shado
|
|||||||
.catalog-summary button.active { border-color: var(--blue); background: var(--blue-soft); }
|
.catalog-summary button.active { border-color: var(--blue); background: var(--blue-soft); }
|
||||||
.catalog-summary strong { color: var(--blue); font-size: 19px; }
|
.catalog-summary strong { color: var(--blue); font-size: 19px; }
|
||||||
.catalog-summary span { color: #748196; font-size: 10px; }
|
.catalog-summary span { color: #748196; font-size: 10px; }
|
||||||
|
.catalog-category-note { margin: -8px 2px 18px; color: #8792a3; font-size: 10px; line-height: 1.6; }
|
||||||
.catalog-toolbar {
|
.catalog-toolbar {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 74px;
|
top: 74px;
|
||||||
@@ -311,6 +312,7 @@ select:focus, input:focus, textarea:focus { border-color: var(--blue); box-shado
|
|||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
}
|
}
|
||||||
.category-pills button.active { border-color: var(--blue); color: white; background: var(--blue); }
|
.category-pills button.active { border-color: var(--blue); color: white; background: var(--blue); }
|
||||||
|
.category-pills button.feature-filter:not(.active) { border-color: #f1bf92; color: #a75a19; background: #fff9f3; }
|
||||||
.catalog-result-count { margin: 11px 2px 0; color: #8792a3; font-size: 10px; }
|
.catalog-result-count { margin: 11px 2px 0; color: #8792a3; font-size: 10px; }
|
||||||
.catalog-result-count strong { color: var(--blue); font-size: 12px; }
|
.catalog-result-count strong { color: var(--blue); font-size: 12px; }
|
||||||
.catalog-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
|
.catalog-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
|
||||||
@@ -492,6 +494,7 @@ footer > span { color: #9aa4b2; font-size: 10px; }
|
|||||||
.capability-strip div:nth-child(2) { border-right: 0; }
|
.capability-strip div:nth-child(2) { border-right: 0; }
|
||||||
.section-heading { align-items: flex-start; flex-direction: column; gap: 18px; }
|
.section-heading { align-items: flex-start; flex-direction: column; gap: 18px; }
|
||||||
.catalog-summary { grid-template-columns: repeat(2, 1fr); }
|
.catalog-summary { grid-template-columns: repeat(2, 1fr); }
|
||||||
|
.catalog-summary button:last-child { grid-column: 1 / -1; }
|
||||||
.catalog-toolbar { top: 64px; }
|
.catalog-toolbar { top: 64px; }
|
||||||
.catalog-grid { grid-template-columns: 1fr; gap: 15px; }
|
.catalog-grid { grid-template-columns: 1fr; gap: 15px; }
|
||||||
.catalog-image-button { height: 280px; }
|
.catalog-image-button { height: 280px; }
|
||||||
|
|||||||
29
app/page.tsx
29
app/page.tsx
@@ -32,18 +32,18 @@ const APPLICATIONS = [
|
|||||||
{ name: "工业设备", note: "控制机构与定制传动组件" },
|
{ name: "工业设备", note: "控制机构与定制传动组件" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const CATALOG_CATEGORIES = [
|
const PRIMARY_CATALOG_CATEGORIES = [
|
||||||
"全部产品",
|
|
||||||
"标准舵机",
|
"标准舵机",
|
||||||
"异形舵机",
|
"异形舵机",
|
||||||
"全金属舵机",
|
"全金属舵机",
|
||||||
"大扭矩舵机",
|
"大扭矩舵机",
|
||||||
"积木舵机",
|
"积木舵机",
|
||||||
"静音舵机",
|
|
||||||
"标准齿轮箱",
|
"标准齿轮箱",
|
||||||
"翻盖电机",
|
"翻盖电机",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const CATALOG_FILTER_CATEGORIES = ["全部产品", ...PRIMARY_CATALOG_CATEGORIES, "静音舵机"];
|
||||||
|
|
||||||
const FEATURED_SPEC_ORDER = [
|
const FEATURED_SPEC_ORDER = [
|
||||||
"堵转扭矩",
|
"堵转扭矩",
|
||||||
"额定电压",
|
"额定电压",
|
||||||
@@ -66,7 +66,6 @@ function getFeaturedSpecs(product: CatalogProduct) {
|
|||||||
const SERVO_CATEGORIES = ["标准舵机", "异形舵机", "全金属舵机", "大扭矩舵机", "积木舵机"];
|
const SERVO_CATEGORIES = ["标准舵机", "异形舵机", "全金属舵机", "大扭矩舵机", "积木舵机"];
|
||||||
const SILENT_SERVO_CATEGORY = "静音舵机";
|
const SILENT_SERVO_CATEGORY = "静音舵机";
|
||||||
const CUSTOM_SERVO_CATEGORY = "非标准舵机定制";
|
const CUSTOM_SERVO_CATEGORY = "非标准舵机定制";
|
||||||
const SELECTOR_CATEGORIES = [...SERVO_CATEGORIES, SILENT_SERVO_CATEGORY, CUSTOM_SERVO_CATEGORY];
|
|
||||||
const TORQUE_MIN = 0.09;
|
const TORQUE_MIN = 0.09;
|
||||||
const TORQUE_MAX = 920;
|
const TORQUE_MAX = 920;
|
||||||
const TORQUE_PRESETS = [0.1, 1, 5, 20, 100, 300, 920];
|
const TORQUE_PRESETS = [0.1, 1, 5, 20, 100, 300, 920];
|
||||||
@@ -446,13 +445,22 @@ export default function Home() {
|
|||||||
onChange={(event) => update("category", event.target.value)}
|
onChange={(event) => update("category", event.target.value)}
|
||||||
>
|
>
|
||||||
<option value="全部舵机">全部舵机(243款)</option>
|
<option value="全部舵机">全部舵机(243款)</option>
|
||||||
{SELECTOR_CATEGORIES.map((category) => (
|
<optgroup label="官方产品系列">
|
||||||
|
{SERVO_CATEGORIES.map((category) => (
|
||||||
|
<option key={category} value={category}>
|
||||||
|
{category}({getCategoryCount(category)}款)
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="特色筛选与定制">
|
||||||
|
{[SILENT_SERVO_CATEGORY, CUSTOM_SERVO_CATEGORY].map((category) => (
|
||||||
<option key={category} value={category}>
|
<option key={category} value={category}>
|
||||||
{category === CUSTOM_SERVO_CATEGORY
|
{category === CUSTOM_SERVO_CATEGORY
|
||||||
? `${category}(联系我们)`
|
? `${category}(联系我们)`
|
||||||
: `${category}(${getCategoryCount(category)}款)`}
|
: `${category}(${getCategoryCount(category)}款)`}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
|
</optgroup>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
<label className="select-field">
|
<label className="select-field">
|
||||||
@@ -836,11 +844,11 @@ export default function Home() {
|
|||||||
<p className="eyebrow"><span /> 官方产品目录 · 2026.06</p>
|
<p className="eyebrow"><span /> 官方产品目录 · 2026.06</p>
|
||||||
<h2>完整收录280个产品型号</h2>
|
<h2>完整收录280个产品型号</h2>
|
||||||
</div>
|
</div>
|
||||||
<p>覆盖标准、异形、静音、全金属、大扭矩和积木舵机,以及标准齿轮箱与翻盖电机。参数来自高徳乐官方产品型号表。</p>
|
<p>官方目录按七个主分类收录280款产品;静音舵机作为特色子集筛选,不重复计入产品总数。</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="catalog-summary" aria-label="产品分类统计">
|
<div className="catalog-summary" aria-label="产品分类统计">
|
||||||
{CATALOG_CATEGORIES.slice(1).map((category) => (
|
{PRIMARY_CATALOG_CATEGORIES.map((category) => (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
key={category}
|
key={category}
|
||||||
@@ -855,6 +863,7 @@ export default function Home() {
|
|||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
<p className="catalog-category-note">分类已按官方目录29页表头复核 · 七个主分类合计280款 · 静音舵机11款为标准舵机特色子集</p>
|
||||||
|
|
||||||
<div className="catalog-toolbar">
|
<div className="catalog-toolbar">
|
||||||
<label className="catalog-search">
|
<label className="catalog-search">
|
||||||
@@ -873,16 +882,16 @@ export default function Home() {
|
|||||||
)}
|
)}
|
||||||
</label>
|
</label>
|
||||||
<div className="category-pills" aria-label="产品类别筛选">
|
<div className="category-pills" aria-label="产品类别筛选">
|
||||||
{CATALOG_CATEGORIES.map((category) => (
|
{CATALOG_FILTER_CATEGORIES.map((category) => (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
key={category}
|
key={category}
|
||||||
className={catalogCategory === category ? "active" : ""}
|
className={`${catalogCategory === category ? "active" : ""} ${category === SILENT_SERVO_CATEGORY ? "feature-filter" : ""}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setCatalogCategory(category);
|
setCatalogCategory(category);
|
||||||
setCatalogLimit(24);
|
setCatalogLimit(24);
|
||||||
}}
|
}}
|
||||||
>{category}</button>
|
>{category === SILENT_SERVO_CATEGORY ? `${category} · ${SILENT_SERVO_COUNT}` : category}</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<p className="catalog-result-count">
|
<p className="catalog-result-count">
|
||||||
|
|||||||
@@ -142,6 +142,12 @@ def main() -> None:
|
|||||||
with pdfplumber.open(PDF_PATH) as pdf:
|
with pdfplumber.open(PDF_PATH) as pdf:
|
||||||
for page_number, page in enumerate(pdf.pages, 1):
|
for page_number, page in enumerate(pdf.pages, 1):
|
||||||
category = CATEGORY_BY_PAGE[page_number]
|
category = CATEGORY_BY_PAGE[page_number]
|
||||||
|
page_text = clean(page.extract_text() or "")
|
||||||
|
expected_heading = f"{category}产品明细表"
|
||||||
|
if expected_heading not in page_text:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Category heading mismatch on page {page_number}: expected {expected_heading}"
|
||||||
|
)
|
||||||
tables = page.find_tables()
|
tables = page.find_tables()
|
||||||
if not tables:
|
if not tables:
|
||||||
raise RuntimeError(f"No table found on page {page_number}")
|
raise RuntimeError(f"No table found on page {page_number}")
|
||||||
|
|||||||
Reference in New Issue
Block a user