Refine catalog modal gallery layout
This commit is contained in:
@@ -467,9 +467,9 @@ select:focus, input:focus, textarea:focus { border-color: var(--blue); box-shado
|
|||||||
}
|
}
|
||||||
.catalog-modal {
|
.catalog-modal {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: min(900px, 100%);
|
width: min(1120px, 100%);
|
||||||
max-height: min(820px, 92vh);
|
max-height: min(820px, 92vh);
|
||||||
padding: 28px;
|
padding: 30px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
border-radius: 18px;
|
border-radius: 18px;
|
||||||
background: white;
|
background: white;
|
||||||
@@ -489,27 +489,24 @@ select:focus, input:focus, textarea:focus { border-color: var(--blue); box-shado
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
.catalog-modal-product { display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(245px, .75fr); align-items: stretch; gap: 25px; }
|
.catalog-modal-product { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(260px, .8fr); align-items: stretch; gap: 28px; }
|
||||||
.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: 9px; }
|
||||||
.catalog-modal-visual-card { min-width: 0; display: grid; grid-template-rows: auto minmax(0, 1fr); gap: 7px; }
|
.catalog-modal-visual-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
|
||||||
.catalog-modal-visual-label { color: #687791; font-size: 10px; font-weight: 800; letter-spacing: .04em; }
|
.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-gallery-switch { display: inline-flex; gap: 3px; padding: 3px; border: 1px solid #dce5f2; border-radius: 8px; background: #f5f8fd; }
|
||||||
.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-gallery-switch button { padding: 6px 9px; border: 0; border-radius: 6px; color: #7c899c; background: transparent; cursor: pointer; font-size: 9px; font-weight: 700; }
|
||||||
.catalog-modal-size-image { background: #fbfcfe; }
|
.catalog-modal-gallery-switch button.active { color: var(--blue); background: white; box-shadow: 0 2px 7px rgba(31,71,139,.09); }
|
||||||
.catalog-modal-size-empty { display: grid; gap: 8px; padding: 18px; text-align: center; }
|
.catalog-modal-gallery-switch button:disabled { cursor: not-allowed; opacity: .4; }
|
||||||
.catalog-modal-size-empty strong { color: #6b7890; font-size: 14px; line-height: 1.5; }
|
.catalog-modal-image { min-height: min(56vh, 520px); display: flex; align-items: center; justify-content: center; border: 1px solid #e1e7f0; border-radius: 13px; overflow: hidden; background: #f5f7fb; }
|
||||||
.catalog-modal-size-empty small { color: #9aa5b5; font-size: 9px; line-height: 1.5; }
|
.catalog-modal-image img { display: block; max-width: 100%; max-height: min(56vh, 510px); width: 100%; height: auto; object-fit: contain; mix-blend-mode: multiply; }
|
||||||
.catalog-modal-product-info { display: flex; flex-direction: column; justify-content: center; min-width: 0; padding: 10px 3px; }
|
.catalog-modal-product-info { display: flex; flex-direction: column; justify-content: flex-start; min-width: 0; padding: 25px 4px 8px; }
|
||||||
.catalog-modal-product h2 { margin: 4px 0 10px; font-size: 27px; line-height: 1.3; letter-spacing: -.025em; }
|
.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-modal-product code { color: #5d6b81; font-size: 11px; }
|
||||||
.catalog-source-note { color: #96a0af; font-size: 9px; }
|
.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-spec-table { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; margin-top: 18px; overflow: hidden; border: 1px solid #e0e5ed; border-radius: 11px; background: #e0e5ed; }
|
||||||
.catalog-modal-highlight span { color: #8a96a9; font-size: 9px; }
|
.catalog-spec-table div { min-height: 38px; padding: 7px 10px; display: grid; align-content: center; gap: 3px; background: white; }
|
||||||
.catalog-modal-highlight strong { color: var(--ink); font-size: 12px; line-height: 1.55; }
|
.catalog-spec-table span { color: #8b96a7; font-size: 8px; }
|
||||||
.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 strong { font-size: 10px; }
|
||||||
.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; }
|
|
||||||
.catalog-spec-table strong { font-size: 11px; }
|
|
||||||
.catalog-modal-actions { display: flex; align-items: center; gap: 18px; margin-top: 20px; }
|
.catalog-modal-actions { display: flex; align-items: center; gap: 18px; margin-top: 20px; }
|
||||||
.catalog-modal-actions .panel-primary { flex: 0 0 auto; }
|
.catalog-modal-actions .panel-primary { flex: 0 0 auto; }
|
||||||
.catalog-modal-actions p { margin: 0; color: #8b95a5; font-size: 9px; line-height: 1.6; }
|
.catalog-modal-actions p { margin: 0; color: #8b95a5; font-size: 9px; line-height: 1.6; }
|
||||||
@@ -574,6 +571,9 @@ footer > span { color: #9aa4b2; font-size: 10px; }
|
|||||||
.catalog-modal-backdrop { padding: 10px; }
|
.catalog-modal-backdrop { padding: 10px; }
|
||||||
.catalog-modal { padding: 22px 16px; }
|
.catalog-modal { padding: 22px 16px; }
|
||||||
.catalog-modal-product { grid-template-columns: 1fr; gap: 16px; }
|
.catalog-modal-product { grid-template-columns: 1fr; gap: 16px; }
|
||||||
|
.catalog-modal-product-info { padding: 0 3px; }
|
||||||
|
.catalog-modal-image { min-height: 290px; }
|
||||||
|
.catalog-modal-image img { max-height: 290px; }
|
||||||
.catalog-spec-table { grid-template-columns: 1fr; }
|
.catalog-spec-table { grid-template-columns: 1fr; }
|
||||||
.catalog-modal-actions { align-items: stretch; flex-direction: column; }
|
.catalog-modal-actions { align-items: stretch; flex-direction: column; }
|
||||||
.products-section { padding: 75px 16px; }
|
.products-section { padding: 75px 16px; }
|
||||||
|
|||||||
73
app/page.tsx
73
app/page.tsx
@@ -31,6 +31,8 @@ type Requirements = {
|
|||||||
painPoint: string;
|
painPoint: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type CatalogVisualMode = "main" | "size";
|
||||||
|
|
||||||
const APPLICATIONS = [
|
const APPLICATIONS = [
|
||||||
{ name: "AI毛绒", note: "头部、耳朵、手臂等轻量动作" },
|
{ name: "AI毛绒", note: "头部、耳朵、手臂等轻量动作" },
|
||||||
{ name: "智能玩具", note: "互动机构、表情和行走结构" },
|
{ name: "智能玩具", note: "互动机构、表情和行走结构" },
|
||||||
@@ -76,16 +78,6 @@ function getFeaturedSpecs(product: CatalogProduct) {
|
|||||||
.map((label) => [label, product.specs[label]] as const);
|
.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 PRECISION_GEAR_CARD_SPEC_ORDER = [
|
const PRECISION_GEAR_CARD_SPEC_ORDER = [
|
||||||
"模数",
|
"模数",
|
||||||
"齿数",
|
"齿数",
|
||||||
@@ -372,6 +364,7 @@ export default function Home() {
|
|||||||
const [precisionGearCategory, setPrecisionGearCategory] = useState("全部子类");
|
const [precisionGearCategory, setPrecisionGearCategory] = useState("全部子类");
|
||||||
const [precisionGearLimit, setPrecisionGearLimit] = useState(24);
|
const [precisionGearLimit, setPrecisionGearLimit] = useState(24);
|
||||||
const [selectedCatalogProduct, setSelectedCatalogProduct] = useState<CatalogProduct | null>(null);
|
const [selectedCatalogProduct, setSelectedCatalogProduct] = useState<CatalogProduct | null>(null);
|
||||||
|
const [catalogVisualMode, setCatalogVisualMode] = useState<CatalogVisualMode>("main");
|
||||||
|
|
||||||
const matches = useMemo(() => getMatches(requirements), [requirements]);
|
const matches = useMemo(() => getMatches(requirements), [requirements]);
|
||||||
const topMatches = matches.slice(0, 3);
|
const topMatches = matches.slice(0, 3);
|
||||||
@@ -425,9 +418,11 @@ export default function Home() {
|
|||||||
const selectedCatalogDimensionImage = selectedCatalogProduct
|
const selectedCatalogDimensionImage = selectedCatalogProduct
|
||||||
? getCatalogProductDimensionImage(selectedCatalogProduct)
|
? getCatalogProductDimensionImage(selectedCatalogProduct)
|
||||||
: null;
|
: null;
|
||||||
const selectedCatalogDimensionSummary = selectedCatalogProduct
|
|
||||||
? getProductDimensionSummary(selectedCatalogProduct)
|
function openCatalogProduct(product: CatalogProduct) {
|
||||||
: "";
|
setSelectedCatalogProduct(product);
|
||||||
|
setCatalogVisualMode("main");
|
||||||
|
}
|
||||||
|
|
||||||
function startSelection() {
|
function startSelection() {
|
||||||
setStage(1);
|
setStage(1);
|
||||||
@@ -866,7 +861,7 @@ export default function Home() {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="recommendation-image-wrap"
|
className="recommendation-image-wrap"
|
||||||
onClick={() => setSelectedCatalogProduct(match.product)}
|
onClick={() => openCatalogProduct(match.product)}
|
||||||
aria-label={`查看${match.product.name}大图`}
|
aria-label={`查看${match.product.name}大图`}
|
||||||
>
|
>
|
||||||
<img src={match.product.image} alt={match.product.name} />
|
<img src={match.product.image} alt={match.product.name} />
|
||||||
@@ -1052,7 +1047,7 @@ export default function Home() {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="catalog-image-button"
|
className="catalog-image-button"
|
||||||
onClick={() => setSelectedCatalogProduct(product)}
|
onClick={() => openCatalogProduct(product)}
|
||||||
aria-label={`查看${product.name}详情`}
|
aria-label={`查看${product.name}详情`}
|
||||||
>
|
>
|
||||||
{product.image ? (
|
{product.image ? (
|
||||||
@@ -1071,7 +1066,7 @@ export default function Home() {
|
|||||||
<div key={label}><dt>{label}</dt><dd>{value}</dd></div>
|
<div key={label}><dt>{label}</dt><dd>{value}</dd></div>
|
||||||
))}
|
))}
|
||||||
</dl>
|
</dl>
|
||||||
<button type="button" onClick={() => setSelectedCatalogProduct(product)}>
|
<button type="button" onClick={() => openCatalogProduct(product)}>
|
||||||
查看完整参数 <span>→</span>
|
查看完整参数 <span>→</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -1162,7 +1157,7 @@ export default function Home() {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="precision-gear-image"
|
className="precision-gear-image"
|
||||||
onClick={() => setSelectedCatalogProduct(product)}
|
onClick={() => openCatalogProduct(product)}
|
||||||
aria-label={`查看${product.name}大图`}
|
aria-label={`查看${product.name}大图`}
|
||||||
>
|
>
|
||||||
<img src={product.image} alt={product.name} loading="lazy" />
|
<img src={product.image} alt={product.name} loading="lazy" />
|
||||||
@@ -1221,31 +1216,37 @@ export default function Home() {
|
|||||||
aria-label="关闭产品详情"
|
aria-label="关闭产品详情"
|
||||||
>×</button>
|
>×</button>
|
||||||
<div className="catalog-modal-product">
|
<div className="catalog-modal-product">
|
||||||
<div className="catalog-modal-visuals">
|
|
||||||
<div className="catalog-modal-visual-card">
|
<div className="catalog-modal-visual-card">
|
||||||
<span className="catalog-modal-visual-label">产品主图</span>
|
<div className="catalog-modal-visual-toolbar">
|
||||||
|
<span className="catalog-modal-visual-label">产品图片</span>
|
||||||
|
<div className="catalog-modal-gallery-switch" role="tablist" aria-label="切换产品图片">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={catalogVisualMode === "main" ? "active" : ""}
|
||||||
|
onClick={() => setCatalogVisualMode("main")}
|
||||||
|
role="tab"
|
||||||
|
aria-selected={catalogVisualMode === "main"}
|
||||||
|
>产品主图</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={catalogVisualMode === "size" ? "active" : ""}
|
||||||
|
onClick={() => setCatalogVisualMode("size")}
|
||||||
|
disabled={!selectedCatalogDimensionImage}
|
||||||
|
role="tab"
|
||||||
|
aria-selected={catalogVisualMode === "size"}
|
||||||
|
>产品尺寸</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="catalog-modal-image">
|
<div className="catalog-modal-image">
|
||||||
{selectedCatalogProduct.image ? (
|
{catalogVisualMode === "size" && selectedCatalogDimensionImage ? (
|
||||||
|
<img src={selectedCatalogDimensionImage} alt={`${selectedCatalogProduct.name} 产品尺寸`} />
|
||||||
|
) : selectedCatalogProduct.image ? (
|
||||||
<img src={selectedCatalogProduct.image} alt={selectedCatalogProduct.name} />
|
<img src={selectedCatalogProduct.image} alt={selectedCatalogProduct.name} />
|
||||||
) : (
|
) : (
|
||||||
<span className="catalog-placeholder">GAUDELOT</span>
|
<span className="catalog-placeholder">GAUDELOT</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</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 className="catalog-modal-product-info">
|
<div className="catalog-modal-product-info">
|
||||||
<p className="step-kicker">{selectedCatalogProduct.category}</p>
|
<p className="step-kicker">{selectedCatalogProduct.category}</p>
|
||||||
<h2 id="catalog-modal-title">{selectedCatalogProduct.name}</h2>
|
<h2 id="catalog-modal-title">{selectedCatalogProduct.name}</h2>
|
||||||
@@ -1257,10 +1258,6 @@ export default function Home() {
|
|||||||
? "商城产品资料 · 舵盘配件"
|
? "商城产品资料 · 舵盘配件"
|
||||||
: "商城产品资料 · 精密齿轮配件"}
|
: "商城产品资料 · 精密齿轮配件"}
|
||||||
</p>
|
</p>
|
||||||
<div className="catalog-modal-highlight">
|
|
||||||
<span>产品尺寸</span>
|
|
||||||
<strong>{selectedCatalogDimensionSummary}</strong>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="catalog-spec-table">
|
<div className="catalog-spec-table">
|
||||||
|
|||||||
Reference in New Issue
Block a user