mcpField
B
b2c-page-designer

b2c-page-designer

github.com/salesforcecommercecloud开发5789 次安装今天更新
代码仓库

关于 b2c-page-designer

用于开发 Salesforce B2C Commerce Page Designer 页面类型和可复用组件类型的技能,介绍文件结构、元数据、属性、渲染及故障排查,并建议依据官方文档核实版本相关细节。

主要功能

  1. 01页面与组件类型结构
  2. 02JSON 元数据及脚本模式
  3. 03属性和区域配置
  4. 04页面与组件渲染指引
  5. 05编辑器不可见及架构问题排查
  6. 06核实版本相关文档

适用场景

  1. 01创建可复用的 Page Designer 组件
  2. 02定义页面区域和组件属性
  3. 03排查组件未显示在编辑器中的原因
  4. 04核对特定平台版本的 API 和架构细节

MCP 相关

该技能建议通过 MCP 使用 `docs_search` 和 `docs_read` 核实 Page Designer 的版本相关文档,但没有说明它提供 MCP 服务。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/salesforcecommercecloud/b2c-developer-tooling --skill b2c-page-designer

安装后在 Claude Code 中输入/b2c-page-designer确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

evals/
trigger-evals.json
references/
ATTRIBUTE-TYPES.md
META-DEFINITIONS.md

详细介绍

页面设计器技能

本技能将指导你为 Salesforce B2C Commerce 创建自定义页面设计器页面类型和组件类型。

范围与依据

本技能涵盖 B2C Commerce 页面设计器(dw.experience 包),并提供与版本无关的模式、JSON 架构结构和代码示例。此处展示的 JSON 架构和 Script API 签名仅供参考,可能会随平台版本变化。在回答涉及特定框架或版本的问题前,或在生成用户将要运行的代码前,请通过 docs_search/docs_read(MCP)或 b2c docs search/b2c docs read(CLI)对照文档确认。对于此处未涵盖的 Script API 方法、属性架构要求和特定版本行为,应以 B2C Commerce 文档为权威依据。

权威文档:

  • dw.experience.PageMgr - 渲染页面和区域
  • dw.experience.PageScriptContext - 页面脚本上下文 API
  • dw.experience.ComponentScriptContext - 组件脚本上下文 API
  • dw.experience.Page - 页面对象方法
  • dw.experience.Component - 组件对象方法
  • dw.experience.Region - 区域对象方法
  • dw.experience.image.Image - 图像属性类型
  • dw.experience - 包概述

概述

页面设计器允许商家通过可视化编辑器创建和管理内容页面。开发人员创建:

  1. 页面类型 - 定义包含区域的页面结构
  2. 组件类型 - 可配置属性的可复用内容块

文件结构

页面设计器文件位于 cartridge 的 experience 目录中:

/my-cartridge
    /cartridge
        /experience
            /pages
                homepage.json           # Page type meta definition
                homepage.js             # Page type script
            /components
                banner.json             # Component type meta definition
                banner.js               # Component type script
        /templates
            /default
                /experience
                    /pages
                        homepage.isml   # Page template
                    /components
                        banner.isml     # Component template

命名: .json 和 .js 文件必须使用相同的名称。experience/pages 或 experience/components 下的文件名和子目录名称只能包含字母数字字符或下划线。

子文件夹中的组件类型: 你可以将组件元数据和脚本放在子目录中(例如 experience/components/assets/)。此时,组件类型 ID 为以点分隔的路径:assets.hero_image_block。templates/default/ 下的模板路径必须与该路径一致(例如 templates/default/experience/components/assets/hero_image_block.isml),脚本则必须调用 Template('experience/components/assets/hero_image_block'),以确保路径匹配。存储的 ID 为 component.{component_type_id};总长度不得超过 256 个字符。

页面类型

元数据定义(pages/homepage.json)

JSON 架构仅供参考;请根据官方平台文档确认特定版本的页面类型架构要求。

{
    "name": "Home Page",
    "description": "Landing page with hero and content regions",
    "region_definitions": [
        {
            "id": "hero",
            "name": "Hero Section",
            "max_components": 1
        },
        {
            "id": "content",
            "name": "Main Content"
        },
        {
            "id": "footer",
            "name": "Footer Section",
            "component_type_exclusions": [
                { "type_id": "video" }
            ]
        }
    ]
}
页面脚本(pages/homepage.js)
'use strict';

var Template = require('dw/util/Template');
var HashMap = require('dw/util/HashMap');
var PageRenderHelper = require('*/cartridge/experience/utilities/PageRenderHelper.js');

module.exports.render = function (context) {
    var model = new HashMap();
    var page = context.page;

    model.page = page;
    model.regions = PageRenderHelper.getRegionModelRegistry(page);

    return new Template('experience/pages/homepage').render(model).text;
};
页面模板(templates/default/experience/pages/homepage.isml)

此处遵循 SFRA 的区域模型模式:页面脚本通过 PageRenderHelper.getRegionModelRegistry(page) 构建 pdict.regions,每次调用 RegionModel.render() 时都会通过 SFRA 的渲染设置委托平台的 PageMgr.renderRegion()。

<isdecorate template="common/layout/page">
    <div class="homepage">
        <div class="hero-region">
            <isprint value="${pdict.regions.hero.render()}" encoding="off"/>
        </div>

        <div class="content-region">
            <isprint value="${pdict.regions.content.render()}" encoding="off"/>
        </div>

        <div class="footer-region">
            <isprint value="${pdict.regions.footer.render()}" encoding="off"/>
        </div>
    </div>
</isdecorate>

组件类型

元数据定义(components/banner.json)

JSON 架构仅供参考;请根据官方平台文档确认特定版本的组件类型架构要求。

{
    "name": "Banner",
    "description": "Promotional banner with image and CTA",
    "group": "content",
    "region_definitions": [],
    "attribute_definition_groups": [
        {
            "id": "image",
            "name": "Image Settings",
            "attribute_definitions": [
                {
                    "id": "image",
                    "name": "Banner Image",
                    "type": "image",
                    "required": true
                },
                {
                    "id": "alt",
                    "name": "Alt Text",
                    "type": "string",
                    "required": true
                }
            ]
        },
        {
            "id": "content",
            "name": "Content",
            "attribute_definitions": [
                {
                    "id": "headline",
                    "name": "Headline",
                    "type": "string",
                    "required": true
                },
                {
                    "id": "body",
                    "name": "Body Text",
                    "type": "markup"
                },
                {
                    "id": "ctaUrl",
                    "name": "CTA Link",
                    "type": "url"
                },
                {
                    "id": "ctaText",
                    "name": "CTA Button Text",
                    "type": "string"
                }
            ]
        },
        {
            "id": "layout",
            "name": "Layout Options",
            "attribute_definitions": [
                {
                    "id": "alignment",
                    "name": "Text Alignment",
                    "type": "enum",
                    "values": ["left", "center", "right"],
                    "default_value": "center"
                },
                {
                    "id": "fullWidth",
                    "name": "Full Width",
                    "type": "boolean",
                    "default_value": false
                }
            ]
        }
    ]
}

组件元数据: 始终包含 region_definitions。当组件没有嵌套区域(即没有用于放置其他组件的插槽)时,请使用 []。

组件脚本(components/banner.js)
'use strict';

var Template = require('dw/util/Template');
var HashMap = require('dw/util/HashMap');
var URLUtils = require('dw/web/URLUtils');

module.exports.render = function (context) {
    var model = new HashMap();
    var content = context.content;

    // Access merchant-configured attributes
    model.put('image', content.image);        // Image object
    model.put('alt', content.alt);            // String
    model.put('headline', content.headline);  // String
    model.put('body', content.body);          // Markup string
    model.put('ctaUrl', content.ctaUrl);      // URL object
    model.put('ctaText', content.ctaText);    // String
    model.put('alignment', content.alignment || 'center');
    model.put('fullWidth', content.fullWidth);

    return new Template('experience/components/banner').render(model).text;
};

模板路径: 传递给 Template(...) 的路径必须与 templates/default/ 下的模板路径一致。如果组件位于子文件夹中(例如 experience/components/assets/hero_image_block),请使用 Template('experience/components/assets/hero_image_block'),并将 ISML 放在 templates/default/experience/components/assets/hero_image_block.isml。

处理颜色(字符串或颜色选择器对象): 如果属性可以是十六进制字符串或颜色选择器对象 { color: "#hex" },请使用一个小型辅助函数,让脚本同时兼容这两种格式:

function getColor(colorAttr) {
    if (!colorAttr) return '';
    if (typeof colorAttr === 'string' && colorAttr.trim()) return colorAttr.trim();
    if (colorAttr.color) return colorAttr.color;
    return '';
}
组件模板(templates/experience/components/banner.isml)
<div class="banner ${pdict.fullWidth ? 'banner--full-width' : ''}"
     style="text-align: ${pdict.alignment}">
    <isif condition="${pdict.image}">
        <img src="${pdict.image.file.absURL}"
             alt="${pdict.alt}"
             class="banner__image"/>
    </isif>

    <div class="banner__content">
        <h2 class="banner__headline">${pdict.headline}</h2>

        <isif condition="${pdict.body}">
            <div class="banner__body">
                <isprint value="${pdict.body}" encoding="off"/>
            </div>
        </isif>

        <isif condition="${pdict.ctaUrl && pdict.ctaText}">
            <a href="${pdict.ctaUrl}" class="banner__cta btn btn-primary">
                ${pdict.ctaText}
            </a>
        </isif>
    </div>
</div>

属性类型

属性架构示例;具体版本的 attribute_definition 语法请以官方平台文档为准。

类型描述返回值
string文本输入字符串
text多行文本字符串
markup富文本编辑器标记语言字符串(使用 encoding="off")
boolean复选框布尔值
integer数字输入整数
enum单选下拉列表字符串或整数(取决于值)
image图片选择器包含 file.absURL 的图片对象
file文件选择器文件对象
urlURL 选择器URL 字符串
category类别选择器类别对象
product商品选择器商品对象
page页面选择器页面对象
customJSON 对象或自定义编辑器对象(或取决于编辑器)

Enum — 对组件可见性至关重要: values 必须使用字符串数组:"values": ["left", "center", "right"]。不要使用 { "value": "x", "display_value": "X" } 这类对象;这种格式可能导致组件类型被拒绝,且不会出现在 Page Designer 组件列表中。如果 values 是字符串,Enum 属性会返回字符串;如果是数字,则会返回整数。

自定义类型和颜色: type: "custom" 搭配例如 editor_definition.type: "styling.colorPicker",需要有一个能在 Business Manager 站点 cartridge 路径中提供该编辑器的 cartridge。如果组件未在编辑器中显示,请将颜色属性的类型改为 type: "string"(由商家输入十六进制值)。在脚本中同时兼容这两种形式:接受字符串或 { color: "#hex" } 这类对象(例如编写一个小型 getColor(attr) 辅助函数,返回字符串)。

default_value: 仅用于店面渲染;在 Page Designer 可视化编辑器中不会显示为预选值。

区域定义

{
    "region_definitions": [
        {
            "id": "main",
            "name": "Main Content",
            "max_components": 10,
            "component_type_exclusions": [
                { "type_id": "heavy-component" }
            ],
            "component_type_inclusions": [
                { "type_id": "text-block" },
                { "type_id": "image-block" }
            ]
        }
    ]
}
属性描述
id唯一的区域标识符
name编辑器中的显示名称
max_components组件数量上限(可选)
component_type_exclusions不允许使用的组件
component_type_inclusions仅允许使用的组件

渲染页面

在控制器中
var PageMgr = require('dw/experience/PageMgr');

server.get('Show', function (req, res, next) {
    var page = PageMgr.getPage(req.querystring.cid);

    if (page && page.isVisible()) {
        res.page(page.ID);
    } else {
        res.setStatusCode(404);
        res.render('error/notfound');
    }
    next();
});
在 ISML 中
<isscript>
    var PageMgr = require('dw/experience/PageMgr');
    var page = PageMgr.getPage('homepage-id');
</isscript>

<isif condition="${page && page.isVisible()}">
    <isprint value="${PageMgr.renderPage(page.ID)}" encoding="off"/>
</isif>

组件分组

在编辑器侧边栏中整理组件:

{
    "name": "Product Card",
    "group": "products"
}

常见分组:content、products、navigation、layout、media

如果组件未出现在 Page Designer 中

  1. 枚举: 确保所有 enum 属性都使用 "values": ["a", "b"](字符串数组),而不是包含 value/display_value 的对象。
  2. 自定义编辑器: 对有问题的属性,将 type: "custom"(例如颜色选择器)替换为 type: "string" 并重新部署;如果组件随后出现,问题可能出在自定义编辑器或 BM cartridge 路径上。
  3. 命名: 文件名和子目录名只能包含字母数字字符或下划线。
  4. region_definitions: 组件 meta 必须包含 region_definitions(如果没有嵌套区域,请使用 [])。
  5. 模板路径: 脚本中的 Template('...') 路径必须与 templates/default/ 下的模板路径一致(包括 experience/components/assets/... 这类子目录)。
  6. 日志: 在 Business Manager 中,打开 Page Designer 时,请检查 Administration > Site Development > Development Setup 中的错误日志,查找与组件类型 ID 相关的脚本或 meta 错误。
  7. 代码版本: 将 cartridge 部署到正确的代码版本;在非生产环境中,meta 可能会缓存几秒钟,可尝试切换代码版本或稍等片刻。

最佳实践

  1. 使用 dw.util.Template 进行渲染(不要使用 dw.template.ISML)
  2. 让组件保持自包含 —— 避免组件间相互依赖
  3. 为可选属性提供默认值(这些默认值适用于渲染;在编辑器中不会显示为预选值)
  4. 在 attribute_definition_groups 中对相关属性进行分组
  5. 使用有意义的 ID —— 它们会以编程方式使用
  6. 商家创建内容后,不要更改类型 ID 或属性类型(这会导致存储的数据不一致);如有需要,可添加新的组件类型并弃用旧类型
  7. 枚举优先使用字符串数组,颜色优先使用字符串,除非你能控制 BM cartridge 路径和自定义编辑器

详细参考

如需完整的属性文档:

评论

支持 Markdown