Improve catalog detail modal with size images

This commit is contained in:
Jacky Ser
2026-08-11 16:22:59 +08:00
parent ca423b3be7
commit 7eed023539
27 changed files with 126 additions and 33 deletions

View File

@@ -24,6 +24,34 @@ const SOURCE_MAIN_IMAGES = {
"313": "/catalog-products/source-main/313-main.png",
} as const;
const SOURCE_SIZE_IMAGES = {
"1004": "/catalog-products/size-reference/1004-size.png",
"258": "/catalog-products/size-reference/258-size.jpg",
"259": "/catalog-products/size-reference/259-size.jpg",
"266": "/catalog-products/size-reference/266-size.jpg",
"267": "/catalog-products/size-reference/267-size.jpg",
"268": "/catalog-products/size-reference/268-size.jpg",
"270": "/catalog-products/size-reference/270-size.jpg",
"271": "/catalog-products/size-reference/271-size.jpg",
"272": "/catalog-products/size-reference/272-size.jpg",
"273": "/catalog-products/size-reference/273-size.jpg",
"274": "/catalog-products/size-reference/274-size.jpg",
"275": "/catalog-products/size-reference/275-size.jpg",
"276": "/catalog-products/size-reference/276-size.jpg",
"308": "/catalog-products/size-reference/308-size.png",
"309": "/catalog-products/size-reference/309-size.png",
"310": "/catalog-products/size-reference/310-size.png",
"311": "/catalog-products/size-reference/311-size.png",
"313": "/catalog-products/size-reference/313-size.png",
"563": "/catalog-products/size-reference/563-size.png",
"565": "/catalog-products/size-reference/565-size.png",
"566": "/catalog-products/size-reference/566-size.png",
"567": "/catalog-products/size-reference/567-size.png",
"568": "/catalog-products/size-reference/568-size.png",
"572": "/catalog-products/size-reference/572-size.png",
} as const;
type ProductImageSet = Record<string, string>;
type SourceImageKey = keyof typeof SOURCE_MAIN_IMAGES;
function hasWeight(name: string, grams: string) {
@@ -37,57 +65,73 @@ function getGearSeries(name: string) {
return null;
}
function getSmallServoImage(name: string): string | null {
if (hasWeight(name, "1\\.7")) return SOURCE_MAIN_IMAGES["1004"];
function getSmallServoImage(name: string, images: ProductImageSet): string | null {
if (hasWeight(name, "1\\.7")) return images["1004"];
const gearSeries = getGearSeries(name);
if (hasWeight(name, "2") && gearSeries === "plastic") return SOURCE_MAIN_IMAGES["272"];
if (hasWeight(name, "3\\.7") && gearSeries === "plastic") return SOURCE_MAIN_IMAGES["271"];
if (hasWeight(name, "2") && gearSeries === "plastic") return images["272"];
if (hasWeight(name, "3\\.7") && gearSeries === "plastic") return images["271"];
if (hasWeight(name, "9")) {
if (gearSeries === "metal") return SOURCE_MAIN_IMAGES["266"];
if (gearSeries === "semi") return SOURCE_MAIN_IMAGES["259"];
if (gearSeries === "plastic") return SOURCE_MAIN_IMAGES["258"];
if (gearSeries === "metal") return images["266"];
if (gearSeries === "semi") return images["259"];
if (gearSeries === "plastic") return images["258"];
}
if (hasWeight(name, "17")) {
if (gearSeries === "metal") return SOURCE_MAIN_IMAGES["274"];
if (gearSeries === "plastic") return SOURCE_MAIN_IMAGES["273"];
if (gearSeries === "metal") return images["274"];
if (gearSeries === "plastic") return images["273"];
}
if (hasWeight(name, "25")) {
if (gearSeries === "metal") return SOURCE_MAIN_IMAGES["276"];
if (gearSeries === "plastic") return SOURCE_MAIN_IMAGES["275"];
if (gearSeries === "metal") return images["276"];
if (gearSeries === "plastic") return images["275"];
}
if (hasWeight(name, "37")) {
if (gearSeries === "metal") return SOURCE_MAIN_IMAGES["270"];
if (gearSeries === "semi") return SOURCE_MAIN_IMAGES["268"];
if (gearSeries === "plastic") return SOURCE_MAIN_IMAGES["267"];
if (gearSeries === "metal") return images["270"];
if (gearSeries === "semi") return images["268"];
if (gearSeries === "plastic") return images["267"];
}
return null;
}
function getBrickServoImage(name: string): string | null {
if (name.includes("3x3x3")) return SOURCE_MAIN_IMAGES["311"];
if (name.includes("3x3x7")) return SOURCE_MAIN_IMAGES["310"];
if (name.includes("3x7x5")) return SOURCE_MAIN_IMAGES["309"];
if (name.includes("6x6x5")) return SOURCE_MAIN_IMAGES["308"];
if (name.includes("11x5x4")) return SOURCE_MAIN_IMAGES["313"];
function getBrickServoImage(name: string, images: ProductImageSet): string | null {
if (name.includes("3x3x3")) return images["311"];
if (name.includes("3x3x7")) return images["310"];
if (name.includes("3x7x5")) return images["309"];
if (name.includes("6x6x5")) return images["308"];
if (name.includes("11x5x4")) return images["313"];
return null;
}
export function getCatalogProductImage(product: CatalogProduct) {
if (product.category === "积木舵机") {
return getBrickServoImage(product.name) ?? product.image;
return getBrickServoImage(product.name, SOURCE_MAIN_IMAGES) ?? product.image;
}
if (product.category === "标准舵机" || product.category === "异形舵机") {
return getSmallServoImage(product.name) ?? product.image;
return getSmallServoImage(product.name, SOURCE_MAIN_IMAGES) ?? product.image;
}
return product.image;
}
export function getCatalogProductDimensionImage(product: CatalogProduct) {
if (product.category === "舵盘配件") {
return SOURCE_SIZE_IMAGES[product.id.replace("shop-", "")] ?? null;
}
if (product.category === "积木舵机") {
return getBrickServoImage(product.name, SOURCE_SIZE_IMAGES);
}
if (product.category === "标准舵机" || product.category === "异形舵机") {
return getSmallServoImage(product.name, SOURCE_SIZE_IMAGES);
}
return null;
}
export const CATALOG_SOURCE_IMAGE_KEYS: SourceImageKey[] = Object.keys(SOURCE_MAIN_IMAGES) as SourceImageKey[];

View File

@@ -458,12 +458,23 @@ select:focus, input:focus, textarea:focus { border-color: var(--blue); box-shado
cursor: pointer;
font-size: 20px;
}
.catalog-modal-product { display: grid; grid-template-columns: 360px 1fr; align-items: center; gap: 34px; }
.catalog-modal-image { min-height: 240px; display: flex; align-items: center; justify-content: center; border-radius: 13px; overflow: hidden; background: #f2f5fa; }
.catalog-modal-image img { display: block; max-width: 100%; max-height: min(55vh, 460px); width: auto; height: auto; object-fit: contain; mix-blend-mode: multiply; }
.catalog-modal-product { display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(245px, .75fr); align-items: stretch; gap: 25px; }
.catalog-modal-visuals { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.catalog-modal-visual-card { min-width: 0; display: grid; grid-template-rows: auto minmax(0, 1fr); gap: 7px; }
.catalog-modal-visual-label { color: #687791; font-size: 10px; font-weight: 800; letter-spacing: .04em; }
.catalog-modal-image, .catalog-modal-size-image { min-height: 250px; display: flex; align-items: center; justify-content: center; border: 1px solid #e1e7f0; border-radius: 13px; overflow: hidden; background: #f5f7fb; }
.catalog-modal-image img, .catalog-modal-size-image img { display: block; max-width: 100%; max-height: min(43vh, 390px); width: auto; height: auto; object-fit: contain; mix-blend-mode: multiply; }
.catalog-modal-size-image { background: #fbfcfe; }
.catalog-modal-size-empty { display: grid; gap: 8px; padding: 18px; text-align: center; }
.catalog-modal-size-empty strong { color: #6b7890; font-size: 14px; line-height: 1.5; }
.catalog-modal-size-empty small { color: #9aa5b5; font-size: 9px; line-height: 1.5; }
.catalog-modal-product-info { display: flex; flex-direction: column; justify-content: center; min-width: 0; padding: 10px 3px; }
.catalog-modal-product h2 { margin: 4px 0 10px; font-size: 27px; line-height: 1.3; letter-spacing: -.025em; }
.catalog-modal-product code { color: #5d6b81; font-size: 11px; }
.catalog-source-note { color: #96a0af; font-size: 9px; }
.catalog-modal-highlight { display: grid; gap: 5px; margin-top: 26px; padding: 13px 14px; border: 1px solid #dbe5f5; border-radius: 11px; background: #f5f8fd; }
.catalog-modal-highlight span { color: #8a96a9; font-size: 9px; }
.catalog-modal-highlight strong { color: var(--ink); font-size: 12px; line-height: 1.55; }
.catalog-spec-table { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; margin-top: 25px; overflow: hidden; border: 1px solid #e0e5ed; border-radius: 11px; background: #e0e5ed; }
.catalog-spec-table div { min-height: 48px; padding: 10px 12px; display: grid; align-content: center; gap: 4px; background: white; }
.catalog-spec-table span { color: #8b96a7; font-size: 9px; }

View File

@@ -7,7 +7,7 @@ import {
type CatalogProduct,
} from "./catalog-data";
import { STEERING_WHEEL_ACCESSORIES } from "./catalog-accessories";
import { getCatalogProductImage } from "./catalog-image-map";
import { getCatalogProductDimensionImage, getCatalogProductImage } from "./catalog-image-map";
const CATALOG_PRODUCTS = [...RAW_CATALOG_PRODUCTS, ...STEERING_WHEEL_ACCESSORIES].map((product) => ({
...product,
@@ -89,6 +89,16 @@ function getFeaturedSpecs(product: CatalogProduct) {
.map((label) => [label, product.specs[label]] as const);
}
function getProductDimensionSummary(product: CatalogProduct) {
const directSize = product.specs["产品尺寸"] ?? product.specs["外形尺寸"];
if (directSize) return directSize;
const dimensions = ["长mm", "宽mm", "高mm"]
.map((label) => product.specs[label])
.filter(Boolean);
return dimensions.length > 0 ? dimensions.join(" × ") : "尺寸图待补充";
}
const SERVO_CATEGORIES = ["标准舵机", "异形舵机", "全金属舵机", "大扭矩舵机", "积木舵机"];
const SILENT_SERVO_CATEGORY = "静音舵机";
const CUSTOM_SERVO_CATEGORY = "非标准舵机定制";
@@ -387,6 +397,12 @@ export default function Home() {
requirements.annualVolume === "50000件以上" ||
Number(requirements.firstOrderQuantity) >= 1000 ||
requirements.application === "工业设备";
const selectedCatalogDimensionImage = selectedCatalogProduct
? getCatalogProductDimensionImage(selectedCatalogProduct)
: null;
const selectedCatalogDimensionSummary = selectedCatalogProduct
? getProductDimensionSummary(selectedCatalogProduct)
: "";
function startSelection() {
setStage(1);
@@ -1088,20 +1104,42 @@ export default function Home() {
aria-label="关闭产品详情"
>×</button>
<div className="catalog-modal-product">
<div className="catalog-modal-image">
{selectedCatalogProduct.image ? (
<img src={selectedCatalogProduct.image} alt={selectedCatalogProduct.name} />
) : (
<span className="catalog-placeholder">GAUDELOT</span>
)}
<div className="catalog-modal-visuals">
<div className="catalog-modal-visual-card">
<span className="catalog-modal-visual-label"></span>
<div className="catalog-modal-image">
{selectedCatalogProduct.image ? (
<img src={selectedCatalogProduct.image} alt={selectedCatalogProduct.name} />
) : (
<span className="catalog-placeholder">GAUDELOT</span>
)}
</div>
</div>
<div className="catalog-modal-visual-card">
<span className="catalog-modal-visual-label"></span>
<div className="catalog-modal-size-image">
{selectedCatalogDimensionImage ? (
<img src={selectedCatalogDimensionImage} alt={`${selectedCatalogProduct.name} 产品尺寸`} />
) : (
<div className="catalog-modal-size-empty">
<strong>{selectedCatalogDimensionSummary}</strong>
<small></small>
</div>
)}
</div>
</div>
</div>
<div>
<div className="catalog-modal-product-info">
<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.page > 0 ? `官方产品目录第 ${selectedCatalogProduct.page}` : "商城产品资料 · 舵盘配件"}
</p>
<div className="catalog-modal-highlight">
<span></span>
<strong>{selectedCatalogDimensionSummary}</strong>
</div>
</div>
</div>
<div className="catalog-spec-table">

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 856 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 954 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB