10 KiB
产品中心
官网产品中心位于 apps/website/app/pages/product-center.vue,访问路径为 /product-center。
默认展示
页面首次加载时展示全部商品和全部国家,不默认选择分类、物流或工艺标签。每页默认请求 20 条商品,用户操作筛选项后再按真实业务 ID 查询。
筛选与分页
- 左侧分类树:选择父分类或子分类。
- 国家:点击国家胶囊筛选,点击“全部”取消国家筛选。
- 物流与工艺:按标签组展示下拉选项,每个分组内暂为单选,不同分组可各选择一项。
- 筛选菜单在选择选项或点击组件外部后自动关闭;已选 Tag 使用独立标签行。仅选择工艺时标签从最左侧开始;物流与工艺同时选择时按物流、工艺顺序与上方筛选项对应排列。
- 搜索:输入关键词后按回车或点击“搜索”。
- 商品卡:整卡链接到
https://inkpod.vip/portal/detail/{商品ID},在新窗口打开。 - 公共商品接口的
id来自originGood.sdsGoodId,即 InkPOD 商品 ID;goods.good_id仅作为本站数据库内部主键使用。 - URL 国家筛选:支持
/product-center?countryId={后台国家ID},仅接受后台已配置的国家 ID。 - 无结果:展示设计稿空状态,可通过“查看全部商品”或“清空筛选”恢复完整列表。
- 加载中:展示两行共八个商品骨架卡,筛选栏保持可操作。
- 分页:支持页码、前后翻页、每页数量和页码跳转。
所有查询通过 useProductCenter.ts 调用 /api/backend/* 代理路由,不在页面中硬编码业务 ID。
后台配置联动
- 品类树、国家、Tag 与 Tag Group 均来自 NestJS
/public/*接口。 - 国家旗帜读取
countryIcon,品类图标读取categoryIcon;前端不提供外部旗帜 CDN 或固定名称兜底。 /uploads/*与/assets/*等后台相对资源地址由useProductCenter.ts根据runtimeConfig.public.backendUrl转为完整 URL。- 物流和工艺筛选分别由后台名称包含“物流”和“工艺”的 Tag Group 生成;“印刷位置”等其他分组仍可用于商品卡片标签,但不会自动成为顶部筛选项。
- Figma 基准图标位于
apps/api/public/product-center/,通过后端/assets/product-center/*提供。 - 更新现有数据库图标配置:
pnpm --filter @inkreach/api configure:product-center-icons。脚本按中文名称幂等更新,不创建缺失的国家或品类。
响应式
桌面采用 240px 分类栏,页面左右保留 24–56px 自适应留白,分类栏可滚动但隐藏可见 scrollbar。商品网格以 240px 为最小卡片宽度自动增加或减少列数:1440px 基准宽度保持四列,1920px 可显示六列。国家筛选区按内容自然增高,不保留固定空白。
左侧品类栏不显示最左侧边线,一级品类之间保留 10px 间距;国家筛选区及物流、工艺、搜索工具区的底部分隔线从内容区向右延伸至页面边缘。
宽度低于 1000px 后分类栏变为抽屉;移动端商品网格降为两列或单列,国家筛选仅在自身区域横向滚动,不会撑宽页面。
多源合并商品(已废弃,由产品族替代)
2026-08-28 起,该机制已被「产品族」完全替代:多链接合并由族承载(尺码/包装并集 + 五维价格矩阵),配置/编辑商品不再写入
mergedOriginGoodIds;good_origin_goods表 只读保留(历史数据仍可命中详情),观察期后删除。以下为历史行为记录:
- 数据层:主源存
goods.origin_good_id,副源存中间表good_origin_goods。 - 详情可达性:官网上通过任一关联原产品的
sdsGoodId都能访问到该商品详情(族机制下 同样成立:族内任何成员链接的 sdsGoodId 均命中同一商品)。
该 Good 级合并能力保留可用;新一级的「产品族(SPU)合并」见下节,公开读路径接入族数据属于三期范围。
产品族(SPU 层,一期后端已上线)
设计文档:docs/superpowers/specs/2026-08-28-product-family-merge-design.md。
模型:SDS 叶子分类即产品模型(如分类 DG001 180G纯棉T恤(JSA002) 下 16 条链接),
族 = 同分类链接的合并体;链接名解析列(国家(物流)品名-SKU-工艺[-仓库])提供
物流/工艺归因。例:DG001 族 = 16 链接 × 3 工艺 × 3 物流 × 9 尺码(S~XXXXXL 并集)× 20 颜色 ≈ 470 格价格矩阵,起价取光板(不打印)最低价。
价格:价格 = f(尺码, 颜色, 物流, 工艺[含印花数量]),全部从成员链接变体推导透传
(同格子多仓库取最低价,来源全保留);family_price_overrides 表支持按格人工改价,
删覆盖即恢复推导价。
关键端点用法示例(JWT):
# 1. 自动成族:先预览
curl -X POST /product-families/auto-group -H "Authorization: Bearer $T" \
-d '{"apply": false}'
# → { applied: 0, groups: [{ groupKey: "cat:8240", familyName: "DG001 180G纯棉T恤(JSA002)", memberCount: 16, ... }] }
# 2. 应用(幂等,可重复执行)
curl -X POST /product-families/auto-group -H "Authorization: Bearer $T" -d '{"apply": true}'
# 3. 族详情:成员 + 并集尺码表/包装 + 五维价格矩阵
curl /product-families/12 -H "Authorization: Bearer $T"
# 4. 人工改价(维度必须存在于族矩阵选项,否则 400 并列出非法键)
curl -X PUT /product-families/12/price-overrides -H "Authorization: Bearer $T" \
-d '{"items": [{"sizeId":"size_S","colorId":"color_blk","craft":"单面印花","logistics":"包邮","price": 23, "note": "促销"}]}'
# 5. 删覆盖恢复推导价
curl -X DELETE /product-families/12/price-overrides -H "Authorization: Bearer $T" \
-d '{"cells": [{"sizeId":"size_S","colorId":"color_blk","craft":"单面印花","logistics":"包邮"}]}'
# 6. 族内创建自定义商品(人工补链接,物流/工艺归因必填)
curl -X POST /product-families/12/members/custom -H "Authorization: Bearer $T" \
-d '{"goodName":"美国(海运)180g纯棉T恤-DG001-烫画","logisticsLabel":"海运","craftLabel":"烫画",
"variants":[{"sku":"C-M","sizeId":"size_M","sizeName":"M","colorId":"color_red","colorName":"红色","price": 33}]}'
同步联动:商品同步落库时刷新解析列;新链接与已有族同分类唯一命中时自动挂族
(多族/零族留给管理员;锁定族只置 stale);详情同步提交后异步重算受影响族
(进程内去重、幂等)。回填/修复脚本:
pnpm --filter @inkreach/api backfill:product-families
# → [1/2] 解析全部链接名 [2/2] 自动建族 [3/3] 全量族重算(幂等,可随时重跑)
公开读路径(三期,默认开启):PUBLIC_DETAIL_FROM_FAMILY=false 可应急回退旧行为。
GET /public/goods/:goodId 行为:
- 变体并集(替代旧副源机制):
variants= 主链接 ∪ 全体族成员 ∪ 旧副源(过渡期), 按(链接, 变体)去重;mediaByColor同源。任何族成员链接的 sdsGoodId 均命中同一商品。 - 族块(默认输出):
family含并集尺码表/包装 + 五维价格矩阵 + 族起价:
{
"family": {
"familyId": "12", "familyCode": "DG015", "familyName": "DG015 180G纯棉T恤",
"sizes": [...], "colors": [...], "crafts": ["单面印花", ...], "logistics": ["包邮", ...],
"sizeChart": { /* 并集 */ }, "packageSpecs": { /* 并集 */ },
"priceMatrix": { /* 五维矩阵:rows 为 {sizeId, colorId, craft, logistics, price, manual, sources} */ },
"minPrice": "29.5"
}
}
实测(DG015 族):4 工艺 × 2 物流 × 8 尺码 × 12 颜色 = 653 格矩阵,族起价 ¥29.5
(主源单链接价为 ¥68.74——族视角展示了光板/单面的更低档价格)。前端本地按五维联动
priceMatrix 即可实时算价,无需新增查价端点(设计 D4)。
边界声明:商品列表/筛选/排序仍基于主源 goodPrice(SQL 层无法廉价解析族矩阵 JSON,
且避免展示价与筛选价不一致);good_origin_goods 转只读保留,观察期后另行删除。
后台操作入口(族替代旧主源/副源,界面保持原有布局):
- 商品配置页布局不变(左树=官网商品、右树=原产品库分类平铺);配置弹窗保持原「合并同名」 勾选流程,提交时静默把勾选链接与主链接归入同一族(无族自动成族);
- 编辑弹窗的原「关联原产品(主源 + 副源)」区块改为「关联原产品(族成员)」:
显示族编码与链接数、成员列表(
设为主链接/移除出族)、搜索添加成员—— 操作直接作用于族(并集与价格矩阵随重算更新); - 人工改价/自动成族等族管理 API(
/product-families/*)保留,供脚本或后续界面使用。
族派生标签(物流/工艺/印刷位置标签自动化):
- 标签组名含「物流」「工艺」「位置」的组视为自动组;有族商品的这些标签由族成员的
logisticsLabel/craftLabel自动生成(前缀匹配取最长命中:单面印花→单面印、 不包邮光板→不包邮),每次族重算/成员变更/商品创建更新时同步到族下所有商品; - 表单中自动组不再出现在标签下拉里(只读展示"族自动:…"),后端也会剔除手动传入的 自动组标签;无族商品(如独立自定义商品)仍可手动打标签;
- 其他分组(如风格类)不受影响,保持人工管理;
- 实测(DG015,10 链接):商品自动获得
包邮、不包邮、双面印、直喷、单面印, 官网物流/工艺筛选直接命中合并后的完整链接集合。
验证
pnpm --filter @inkreach/website test --run
pnpm --filter @inkreach/website build
pnpm --filter @inkreach/website dev
pnpm --filter @inkreach/api test -- --runInBand
pnpm --filter @inkreach/api build