Shopify 开发者集成#
将 ideal.house 按钮添加到 Shopify 主题并将其映射到已选变体 SKU。
<!-- DASHBOARD_READINESS -->加载 SDK 之前: 打开 Dashboard → 设置,将您的店铺前台来源添加到 Allowed Origins (CORS) 中,然后点击 Save。包含发布的hostname以及任何预览或预发环境来源;多个来源用逗号分隔。参见配置步骤。
1. 确认前提条件#
- 你可以编辑实时主题代码或主题的安全副本。
- 至少有一个测试商品已导入并在 Catalog 中处于激活状态。
- 已选变体 SKU 是目录为该 Shop 使用的相同持久化 productCode。
2. 创建按钮片段#
snippets/idealhouse-view-button.liquid#
{%- comment -%}
Renders an ideal.house authored visualizer button.
Accepts:
- product_code: {String} Variant SKU used by the ideal.house Catalog mapping.
- placement: {String} "card", "pdp-desktop", or "pdp-mobile".
{%- endcomment -%}
{%- liquid
assign button_code = product_code | strip
assign button_placement = placement | default: 'card'
-%}
<button
type="button"
class="idealhouse-view-button idealhouse-view-button--{{ button_placement | escape }}"
data-idealhouse-authored-button
data-active="false"
{% if button_code != blank %}
data-product-code="{{ button_code | escape }}"
{% else %}
disabled
{% endif %}
style="visibility: hidden;"
>
<span>View in my room</span>
</button>
保持按钮隐藏,直到 SDK 确认商品符合资格。
3. 在主题界面中渲染按钮#
合集和搜索结果卡片#
{%- render 'idealhouse-view-button',
product_code: card_product.selected_or_first_available_variant.sku,
placement: 'card'
-%}
常见文件包括 snippets/card-product.liquid、snippets/product-card.liquid 或 snippets/product-grid-item.liquid。请将 card_product 适配为主题所使用的商品变量。
商品详情页面#
{%- render 'idealhouse-view-button',
product_code: product.selected_or_first_available_variant.sku,
placement: 'pdp-desktop'
-%}
{%- render 'idealhouse-view-button',
product_code: product.selected_or_first_available_variant.sku,
placement: 'pdp-mobile'
-%}
常见文件包括 sections/main-product.liquid 或 snippets/product.liquid。仅当主题具有独立的桌面和移动布局时才分别放置。
4. 加载 SDK 并同步变体#
assets/idealhouse-room-visualizer.js#
(function () {
'use strict';
var buttonSelector = '[data-idealhouse-authored-button]';
var sdkRequested = false;
var buttonObserver = null;
function resetButton(button) {
button.dataset.active = 'false';
button.style.visibility = 'hidden';
delete button.dataset.rvProductCode;
delete button.dataset.rvProductType;
delete button.dataset.rvProductId;
delete button.dataset.rvProductIds;
}
function syncButton(button, productCode) {
if (!button) return false;
var code = typeof productCode === 'string' ? productCode.trim() : '';
resetButton(button);
delete button.dataset.productCode;
if (!code) {
button.disabled = true;
return false;
}
button.disabled = false;
button.dataset.productCode = code;
return true;
}
function syncButtons(container, productCode) {
(container || document).querySelectorAll(buttonSelector).forEach(function (button) {
syncButton(button, productCode);
});
}
function loadSdk() {
if (sdkRequested || !document.querySelector(buttonSelector)) return false;
sdkRequested = true;
if (buttonObserver) buttonObserver.disconnect();
var script = document.createElement('script');
script.src = 'https://sdk.ideal.house/sdk.js';
script.async = true;
script.dataset.shopId = '<SHOP_ID>';
script.dataset.publishableKey = '<PUBLISHABLE_KEY>';
script.dataset.authoredButtonSelector = buttonSelector;
document.head.appendChild(script);
return true;
}
function startSdkLoader() {
if (loadSdk() || typeof MutationObserver === 'undefined') return;
buttonObserver = new MutationObserver(loadSdk);
buttonObserver.observe(document.documentElement, { childList: true, subtree: true });
}
// Dispatch this event from your theme's variant-change callback.
document.addEventListener('idealhouse:variant-change', function (event) {
var detail = event.detail || {};
syncButtons(detail.container || document, event.detail && event.detail.sku);
});
window.IdealhouseShopify = {
syncButton: syncButton,
syncButtons: syncButtons
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', startSdkLoader);
} else {
startSdkLoader();
}
}());
Shopify 未定义统一的主题变体事件。将下方的小型适配器连接到活跃主题暴露的事件或回调。
主题事件适配器示例#
将示例事件替换为当前主题 API。部分主题通过 window.subscribe 发布事件;其他主题会派发 DOM 事件或更新 product-info。
// Example only: replace "variant:change" with your theme's event.
document.addEventListener('variant:change', function (event) {
var variant = event.detail && event.detail.variant;
var productContainer = event.target.closest('product-info') || document;
document.dispatchEvent(new CustomEvent('idealhouse:variant-change', {
detail: {
container: productContainer,
sku: variant && variant.sku
}
}));
});
5. 添加主题样式#
assets/idealhouse-room-visualizer.css#
.idealhouse-view-button {
align-items: center;
justify-content: center;
width: 100%;
min-height: 44px;
padding: 0.75rem 1rem;
border: 1px solid currentColor;
border-radius: 6px;
background: transparent;
color: inherit;
cursor: pointer;
font: inherit;
}
.idealhouse-view-button[data-active="true"] {
display: inline-flex;
visibility: visible;
}
.idealhouse-view-button:disabled {
cursor: not-allowed;
opacity: 0.5;
}
主题可能会覆盖这些视觉样式。
6. 将资源包含在 theme.liquid 中#
在 layout/theme.liquid 中包含可视化样式表和脚本,以应用样式并激活 SDK 加载器。
包含样式表(在 <head> 内)#
{{ 'idealhouse-room-visualizer.css' | asset_url | stylesheet_tag }}
放在 layout/theme.liquid 的 <head> 标签内,以确保样式干净加载。
包含脚本(在 </body> 之前)#
<script src="{{ 'idealhouse-room-visualizer.js' | asset_url }}" defer="defer"></script>
使用 defer 属性,并将其放在 layout/theme.liquid 中关闭 </body> 标签之前,以避免阻塞页面渲染。
「在我的房间查看」按钮在此加载器脚本包含在 theme.liquid 之前一直处于隐藏状态。
7. 在实时店铺前台上验证#
- Network 面板显示 sdk.js 加载成功,HTTP 状态码为 200。
- 手动编写的按钮 data-product-code 与 Catalog productCode 完全匹配。
- 符合条件的商品会显示按钮;不符合的商品保持隐藏。
- 切换 Variant 会更新 data-product-code 并重新验证按钮。
- 合集/搜索、PDP 桌面和 PDP 移动端的放置位置在相应位置均能工作。
- 点击按钮会打开 ideal.house Viewer 并展示所选商品。