参考网站
Add subscriptions to your theme
预览
前置条件
需创建多个同样但价格不一的产品
需要以当前产品创建多个产品,来设置捆绑数量,比如1件单独销售、2件起售、n件起售,享受不同的折扣和销售计划的折扣。(本质上还是一件产品,不同的价格)
如果是一个产品的销售计划直接复制参考网站代码
安装app创建销售计划
需要安装Shopify官方提供的Subscriptions设置产品订阅计划
设置过后插件通过admin api的muation,这样商店产品就有了“销售计划”这个属性。
创建产品元字段
主要是为了能把默认产品和具有订阅的产品相关联
代码实现
原理
shopify默认加购是form表单,然后使用ajax向shopify发送加购请求body是一个FormData,只需要添加隐藏的input,name为selling_plan,value是selling_plan的id即可。
技术实现
创建selling-plans-integration.liquid(snippets)、selling-plan.css(assets)、selling-plan.js(assets)
创建一个buy-buttons副本buy-selling-buttons,避免影响全局的buy-buttons,
buy-selling-buttons 里面循环元子字段绑定的产品,进而创建顶部tab,再同样循环加购表单,在表单里面render selling-plans-integration.liquid,然后点击tab显示隐藏订阅即可。
我们把所有值都绑定在select的option里(不同的订阅plan,如按周、月、年订阅),切换option拿到值动态渲染,点击一次性加购选项清空所有表单selling_plan的value值,点击订阅选项找到所有表单的select选中的option绑定在dom上的selling_plan的id并赋予到selling_plan这个隐藏的input上,就是这么个逻辑。
代码
selling-plans-integration.liquid
{%- assign current_variant = product.selected_or_first_available_variant | default: product.variants.first -%}
{% if product.selling_plan_groups.size > 0 %}
<div class="selling_plan_app_container" data-section-id='{{ section.id }}'>
{% for variant in product.variants %}
{% if variant.selling_plan_allocations.size > 0 %}
<section data-variant-id='{{ variant.id }}' class='selling_plan_theme_integration {% if variant.id != current_variant.id %}selling_plan_theme_integration--hidden{% endif %}'>
<fieldset>
<div>
<div class="radio-container">
<div class="radio-container--subscribe">
<label>
<input
value="sub"
type='radio'
name="purchaseOption_{{ section.id }}_{{ variant.id }}"
{% if variant.available == false %}disabled{% endif %}
data-variant-id='{{ variant.id }}'
data-variant-saving ='{{ variantSavingPrice }}'
checked
/>
Subscribe & Save
</label>
</div>
{% unless product.requires_selling_plan %}
<div class="radio-container--one-time">
<label>
<input
value="one"
type='radio'
name="purchaseOption_{{ section.id }}_{{ variant.id }}"
/>
One-time purchase
</label>
</div>
{% endunless %}
</div>
{% assign variant_selling_price = product.selected_or_first_available_selling_plan_allocation.price %}
{% assign variant_compare_at_price = product.selected_or_first_available_variant.compare_at_price %}
{% assign variant_price = product.selected_or_first_available_variant.price %}
<div class="price-wrapper">
<div class="price-container selling-price">
{% if variant_selling_price == variant_price %}
<div class="price-regular">{{ variant_selling_price | money }}</div>
{% else %}
<div class="price-sale">
<div class="price-item--sale">{{ variant_selling_price | money }}</div>
<s class="price-item--regular">
{% if variant_compare_at_price %}
{{ variant_compare_at_price | money }}
{% else %}
{{ variant_price | money }}
{% endif %}
</s>
{% if variant_compare_at_price %}
{% assign real_compare_price = variant_compare_at_price %}
{% else %}
{% assign real_compare_price = variant_price %}
{% endif %}
{% assign saveing = real_compare_price| minus: variant_selling_price %}
<span class="save-price">Save {{ saveing | money }}</span>
</div>
{% endif %}
</div>
<div class="price-container product-price">
{% if variant_price == variant_compare_at_price or variant_compare_at_price == blank %}
<div class="price-regular">{{ variant_price | money }}</div>
{% else %}
<div class="price-sale">
<div class="price-item--sale">{{ variant_price | money }}</div>
<s class="price-item--regular">{{ variant_compare_at_price| money }}</s>
</div>
{% endif %}
</div>
</div>
<div class="policy-wrapper">
<div class="policy-advantage">
<ul>
<li>
<svg t="1744189199600" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="20488" width="24" height="24"><path d="M512 1024C229.239467 1024 0 794.760533 0 512 0 229.239467 229.239467 0 512 0c282.760533 0 512 229.239467 512 512 0 282.760533-229.239467 512-512 512z m245.521067-707.413333l-298.939734 275.387733-169.984-147.421867-38.263466 39.662934 208.145066 230.6048 331.229867-365.226667-32.187733-33.006933z" fill="#00629b" p-id="20489"></path></svg>
<b>Free</b> shipping on every delivery</li>
<li>
<svg t="1744189199600" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="20488" width="24" height="24"><path d="M512 1024C229.239467 1024 0 794.760533 0 512 0 229.239467 229.239467 0 512 0c282.760533 0 512 229.239467 512 512 0 282.760533-229.239467 512-512 512z m245.521067-707.413333l-298.939734 275.387733-169.984-147.421867-38.263466 39.662934 208.145066 230.6048 331.229867-365.226667-32.187733-33.006933z" fill="#00629b" p-id="20489"></path></svg>
<b>Free</b> surprise gift with 2nd delivery
</li>
<li>
<svg t="1744189199600" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="20488" width="24" height="24"><path d="M512 1024C229.239467 1024 0 794.760533 0 512 0 229.239467 229.239467 0 512 0c282.760533 0 512 229.239467 512 512 0 282.760533-229.239467 512-512 512z m245.521067-707.413333l-298.939734 275.387733-169.984-147.421867-38.263466 39.662934 208.145066 230.6048 331.229867-365.226667-32.187733-33.006933z" fill="#00629b" p-id="20489"></path></svg>
<b>Cancel, pause or modify anytime</b>
</li>
</ul>
</div>
<div class="policy-not-advantage">
<div>
<p><b>Not ready to subscribe?</b></p>
<p><b>Here's what you'll miss out on:</b></p>
</div>
<ul>
{% assign extra_money = variant_price | minus: variant_selling_price %}
<li>
<svg t="1744189679350" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="28386" width="18" height="18"><path d="M512 981.333333C251.733333 981.333333 42.666667 772.266667 42.666667 512S251.733333 42.666667 512 42.666667s469.333333 209.066667 469.333333 469.333333-209.066667 469.333333-469.333333 469.333333z m0-409.6l98.133333 98.133334c8.533333 8.533333 17.066667 12.8 29.866667 12.8 12.8 0 21.333333-4.266667 29.866667-12.8 17.066667-17.066667 17.066667-42.666667 0-59.733334L571.733333 512l98.133334-98.133333c8.533333-8.533333 12.8-17.066667 12.8-29.866667 0-12.8-4.266667-21.333333-12.8-29.866667-8.533333-8.533333-17.066667-12.8-29.866667-12.8-12.8 0-21.333333 4.266667-29.866667 12.8L512 452.266667 413.866667 354.133333C405.333333 345.6 396.8 341.333333 384 341.333333c-12.8 0-21.333333 4.266667-29.866667 12.8-8.533333 8.533333-12.8 17.066667-12.8 29.866667 0 12.8 4.266667 21.333333 12.8 29.866667l98.133334 98.133333-98.133334 98.133333c-17.066667 17.066667-17.066667 42.666667 0 59.733334 8.533333 8.533333 17.066667 12.8 29.866667 12.8 12.8 0 21.333333-4.266667 29.866667-12.8l98.133333-98.133334z" p-id="28387" fill="#e16531"></path></svg>
<b>{{ extra_money | money }}</b> in extra savings</li>
<li>
<svg t="1744189679350" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="28386" width="18" height="18"><path d="M512 981.333333C251.733333 981.333333 42.666667 772.266667 42.666667 512S251.733333 42.666667 512 42.666667s469.333333 209.066667 469.333333 469.333333-209.066667 469.333333-469.333333 469.333333z m0-409.6l98.133333 98.133334c8.533333 8.533333 17.066667 12.8 29.866667 12.8 12.8 0 21.333333-4.266667 29.866667-12.8 17.066667-17.066667 17.066667-42.666667 0-59.733334L571.733333 512l98.133334-98.133333c8.533333-8.533333 12.8-17.066667 12.8-29.866667 0-12.8-4.266667-21.333333-12.8-29.866667-8.533333-8.533333-17.066667-12.8-29.866667-12.8-12.8 0-21.333333 4.266667-29.866667 12.8L512 452.266667 413.866667 354.133333C405.333333 345.6 396.8 341.333333 384 341.333333c-12.8 0-21.333333 4.266667-29.866667 12.8-8.533333 8.533333-12.8 17.066667-12.8 29.866667 0 12.8 4.266667 21.333333 12.8 29.866667l98.133334 98.133333-98.133334 98.133333c-17.066667 17.066667-17.066667 42.666667 0 59.733334 8.533333 8.533333 17.066667 12.8 29.866667 12.8 12.8 0 21.333333-4.266667 29.866667-12.8l98.133333-98.133334z" p-id="28387" fill="#e16531"></path></svg>
<b>Free</b> shipping</li>
<li>
<svg t="1744189679350" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="28386" width="18" height="18"><path d="M512 981.333333C251.733333 981.333333 42.666667 772.266667 42.666667 512S251.733333 42.666667 512 42.666667s469.333333 209.066667 469.333333 469.333333-209.066667 469.333333-469.333333 469.333333z m0-409.6l98.133333 98.133334c8.533333 8.533333 17.066667 12.8 29.866667 12.8 12.8 0 21.333333-4.266667 29.866667-12.8 17.066667-17.066667 17.066667-42.666667 0-59.733334L571.733333 512l98.133334-98.133333c8.533333-8.533333 12.8-17.066667 12.8-29.866667 0-12.8-4.266667-21.333333-12.8-29.866667-8.533333-8.533333-17.066667-12.8-29.866667-12.8-12.8 0-21.333333 4.266667-29.866667 12.8L512 452.266667 413.866667 354.133333C405.333333 345.6 396.8 341.333333 384 341.333333c-12.8 0-21.333333 4.266667-29.866667 12.8-8.533333 8.533333-12.8 17.066667-12.8 29.866667 0 12.8 4.266667 21.333333 12.8 29.866667l98.133334 98.133333-98.133334 98.133333c-17.066667 17.066667-17.066667 42.666667 0 59.733334 8.533333 8.533333 17.066667 12.8 29.866667 12.8 12.8 0 21.333333-4.266667 29.866667-12.8l98.133333-98.133334z" p-id="28387" fill="#e16531"></path></svg>
<b>Free</b> surprise gift with 2nd delivery</li>
</ul>
</div>
</div>
{% assign group_ids = variant.selling_plan_allocations | map: 'selling_plan_group_id' | uniq %}
{% for group_id in group_ids %}
{%liquid
assign group = product | map: 'selling_plan_groups' | where: 'id', group_id | first
assign allocations = variant | map: 'selling_plan_allocations' | where: 'selling_plan_group_id', group_id
if forloop.first
assign first_selling_plan_group = true
else
assign first_selling_plan_group = false
endif
%}
<div class="selling-select-container">
<select>
{% for allocation in allocations %}
{%liquid
if forloop.first and product.requires_selling_plan and first_selling_plan_group
assign plan_checked = 'checked'
else
assign plan_checked = nil
endif
assign allocationPrice = allocation.price | money | escape
assign allocationSavingPrice = real_compare_price | minus: allocation.price | money | escape
%}
<option
{% if variant.available == false %}disabled{% endif %}
aria-label='{{ allocation.selling_plan.name }}. Product price {{ allocationPrice }}'
data-radio-type='selling_plan'
data-selling-plan-id='{{ allocation.selling_plan.id }}'
data-selling-plan-group-id='{{ section.id }}_{{ group_id }}_{{ variant.id }}'
data-selling-plan-adjustment='{{ allocation.selling_plan.price_adjustments.size }}'
data-variant-price='{{ allocationPrice }}'
data-variant-saving='{{ allocationSavingPrice }}'
{{ plan_checked }}
>
{{ allocation.selling_plan.name | split: ',' | first }}
</option>
{% endfor %}
</select>
</div>
{% endfor %}
</div>
</fieldset>
</section>
{% endif %}
{% endfor %}
<input
name='selling_plan'
class='selected-selling-plan-id'
value="{{ product.selected_or_first_available_variant.selling_plan_allocations.first.selling_plan.id }}"
type='hidden'
>
</div>
{% endif %}
selling-plan.css
.product-selling-plan-form {
color: #000;
}
.product-selling-plan-form .product-form {
margin: 0;
}
.product-selling-plan-form fieldset {
border: none;
padding: 0;
}
.product-selling-plan-form .selling-name-list__item .label-flare {
position: absolute;
top: 0;
width: 100%;
background-color: #e4eef6;
font-size: 12px;
font-weight: 700;
line-height: 1;
color: #00629b;
text-transform: uppercase;
text-wrap: nowrap;
padding: 4px;
}
.product-selling-plan-form .selling-name-list__item.selected .label-flare {
background-color: #00629b;
color: #fff;
}
.product-selling-plan-form .selling-name-list__item .label-title {
font-weight: bold;
font-size: 18px;
}
.product-selling-plan-form .selling-name-list__item {
overflow: hidden;
flex: 1;
position: relative;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
cursor: pointer;
background-color: #fcfeff;
border: 1px solid #E4EEF6;
border-radius: 6px;
text-align: center;
}
.product-selling-plan-form .selling-name-list__item.has-tag {
padding: 20px 10px 0px;
}
.product-selling-plan-form .bottom-price {
font-size: 14px;
}
.product-selling-plan-form .selling-name-list__item.selected {
background-color: #f3faff;
border-color: #00629b;
}
.product-selling-plan-form .price-wrapper {
min-height: 40px;
margin: 20px 0;
}
.product-selling-plan-form .product-form-list {
margin-top: 20px;
background-color: #F4FAFF;
border-radius: 4px;
padding: 10px;
}
.product-selling-plan-form .product-form {
display: none;
pointer-events: none;
}
.product-selling-plan-form .product-form.show {
pointer-events: auto;
display: block;
}
.product-selling-plan-form.onetime-form .selling-name-list .one-price,
.product-selling-plan-form.subscription-form .selling-name-list .selling-save-price,
.product-selling-plan-form.onetime-form .policy-not-advantage,
.product-selling-plan-form.subscription-form .policy-advantage,
.product-selling-plan-form.onetime-form .product-price,
.product-selling-plan-form.subscription-form .selling-select-container,
.product-selling-plan-form.subscription-form .selling-price {
display: block;
}
.product-selling-plan-form.onetime-form .selling-name-list .selling-save-price,
.product-selling-plan-form.subscription-form .selling-name-list .one-price,
.product-selling-plan-form.subscription-form .policy-not-advantage,
.product-selling-plan-form.onetime-form .policy-advantage,
.product-selling-plan-form.onetime-form .selling-price,
.product-selling-plan-form.onetime-form .selling-select-container,
.product-selling-plan-form.subscription-form .product-price {
display: none;
}
.product-selling-plan-form.subscription-form .radio-container--subscribe {
font-weight: 600;
}
.product-selling-plan-form.onetime-form .radio-container--subscribe {
font-weight: 600;
}
.product-selling-plan-form .selling-name-list {
display: flex;
gap: 20px;
}
.product-selling-plan-form .price-sale {
display: flex;
align-items: center;
gap: 10px;
}
.product-selling-plan-form .price-sale > span {
margin-left: 10px;
background-color: #ced1ff;
padding: 5px 10px;
font-weight: 600;
text-align: center;
border-radius: 2px;
}
.product-selling-plan-form .selling-select-container {
margin: 20px 0;
}
.product-selling-plan-form select {
display: block;
width: 100%;
height: 40px;
font-size: 14px;
padding: 0 20px;
appearance: none;
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: right 20px center;
background-size: 16px;
}
.product-selling-plan-form .radio-container {
display: flex;
}
.product-selling-plan-form .radio-container label {
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
}
.product-selling-plan-form .radio-container > div {
flex: 1;
}
.product-selling-plan-form .radio-container > div input {
width: 16px;
height: 16px;
accent-color: #00629b;
cursor: pointer;
margin: 0;
}
.product-selling-plan-form .price-regular,
.product-selling-plan-form .price-item--sale,
.product-selling-plan-form .price-item--regular {
font-weight: bold;
font-size: 24px;
min-height: 40px;
}
.product-selling-plan-form .price-sale .price-item--regular {
color: #666;
}
.policy-not-advantage,
.policy-advantage {
margin: 20px 0;
}
.policy-not-advantage p {
margin: 0;
}
.policy-not-advantage ul {
margin-top: 10px;
}
.policy-wrapper li {
display: flex;
align-items: center;
list-style: none;
gap: 4px;
}
.policy-wrapper li svg {
margin-right: 6px;
width: 18px;
height: 18px;
}
.policy-wrapper ul {
padding: 0;
display: flex;
flex-direction: column;
gap: 8px;
}
selling-plan.js
class ProductSellingPlanHandler {
constructor() {
this.radioContainers = document.querySelectorAll('.product-selling-plan-form .radio-container');
this.productSellingPlanForm = document.querySelector('.product-selling-plan-form');
this.sellingNameListItem = document.querySelectorAll('.selling-name-list__item');
this.selectContainers = document.querySelectorAll('.selling-select-container');
this.init();
}
init() {
this.setupRadioChangeEvents();
this.setupSellingNameListItemClickEvents();
this.setupSelectChangeEvents();
}
setupRadioChangeEvents() {
this.radioContainers.forEach(container => {
const radios = container.querySelectorAll('input[type="radio"]');
radios.forEach(radio => {
radio.addEventListener('change', () => {
this.handleRadioChange(radio);
});
});
});
}
handleRadioChange(radio) {
const value = radio.value;
if (value === 'one') {
this.productSellingPlanForm.classList.add('onetime-form');
this.productSellingPlanForm.classList.remove('subscription-form');
this.clearSellingInputValue();
} else {
this.productSellingPlanForm.classList.add('subscription-form');
this.productSellingPlanForm.classList.remove('onetime-form');
this.setSellingInputValue();
}
const isChecked = radio.checked;
this.radioContainers.forEach(otherContainer => {
const otherRadios = otherContainer.querySelectorAll('input[type="radio"]');
otherRadios.forEach(otherRadio => {
if (otherRadio.value === value) {
otherRadio.checked = isChecked;
}
});
});
}
setupSellingNameListItemClickEvents() {
this.sellingNameListItem.forEach(el => {
el.addEventListener('click', () => {
this.handleSellingNameListItemClick(el);
});
});
}
handleSellingNameListItemClick(el) {
this.sellingNameListItem.forEach(item => item.classList.remove('selected'));
el.classList.add('selected');
const productId = el.getAttribute('data-product-id');
const forms = document.querySelectorAll('.product-form');
forms.forEach(form => {
if (form.getAttribute('data-product-id') === productId) {
form.classList.add('show');
} else {
form.classList.remove('show');
}
});
}
setupSelectChangeEvents() {
this.selectContainers.forEach((container, idx) => {
const select = container.querySelector('select');
select.addEventListener('change', () => {
this.handleSelectChange(select, idx);
});
});
}
handleSelectChange(select, idx) {
const selectedOption = select.options[select.selectedIndex];
const sellingPlanId = selectedOption.getAttribute('data-selling-plan-id');
const productForm = select.closest('.product-form');
const selectedSellingPlanIdElement = productForm.querySelector('.selected-selling-plan-id');
selectedSellingPlanIdElement.value = sellingPlanId;
const price = selectedOption.dataset.variantPrice;
const saving = selectedOption.dataset.variantSaving;
const priceItemSale = productForm.querySelector('.price-item--sale');
const priceItemSaving = productForm.querySelector('.save-price');
priceItemSale.innerHTML = price;
priceItemSaving.innerHTML = `Save ${saving}`;
this.sellingNameListItem.forEach((el, i) => {
if (idx === i) {
el.querySelector('.selling-save-price').innerHTML = `Save ${saving}`;
}
});
}
clearSellingInputValue() {
const inputContainers = document.querySelectorAll('.selected-selling-plan-id');
inputContainers.forEach(input => {
input.value = '';
});
}
setSellingInputValue() {
const inputContainers = document.querySelectorAll('.selected-selling-plan-id');
inputContainers.forEach(input => {
const sellingPlanAppContainer = input.closest('.selling_plan_app_container');
if (sellingPlanAppContainer) {
const selectedOption = sellingPlanAppContainer.querySelector('select');
if (selectedOption) {
const selectedOptionId = selectedOption.options[selectedOption.selectedIndex].getAttribute('data-selling-plan-id');
input.value = selectedOptionId;
}
}
});
}
}
new ProductSellingPlanHandler();
buy-selling-buttons.liquid
{% comment %}
Renders product buy-buttons.
Accepts:
- product: {Object} product object.
- block: {Object} passing the block information.
- product_form_id: {String} product form id.
- section_id: {String} id of section to which this snippet belongs.
- show_pickup_availability: {Boolean} for the pickup availability. If true the pickup availability is rendered, false - not rendered (optional).
Usage:
{% render 'buy-buttons', block: block, product: product, product_form_id: product_form_id, section_id: section.id, show_pickup_availability: true %}
{% endcomment %}
{{ 'selling-plan.css' | asset_url | stylesheet_tag }}
<div class="product-selling-plan-form subscription-form">
{%- if product != blank -%}
{% if product.metafields.custom.selling_plan_products != blank %}
<div class="selling-name-list">
{% for product_item in product.metafields.custom.selling_plan_products.value %}
{% if product_item.selected_or_first_available_variant.compare_at_price %}
{% assign real_compare_price = product_item.selected_or_first_available_variant.compare_at_price %}
{% else %}
{% assign real_compare_price = product_item.selected_or_first_available_variant.price %}
{% endif %}
{% assign saveing = real_compare_price | minus: product_item.selected_or_first_available_selling_plan_allocation.price %}
<legend data-product-id="{{ product_item.id }}" class="selling-name-list__item {% if product_item.metafields.custom.selling_tag %}has-tag{% endif %} {% if forloop.first %}selected{% endif %}">
{% if product_item.metafields.custom.selling_tag %}
<span class="label-flare">{{ product_item.metafields.custom.selling_tag }}</span>
{% endif %}
<span class="label-title">{{ product_item.metafields.custom.selling_name }}</span>
<span class="selling-save-price bottom-price">
Save {{ saveing | money }}
</span>
<span class="one-price bottom-price">
{% if product_item.selected_or_first_available_variant.compare_at_price > product_item.selected_or_first_available_variant.price %}
{% assign saveing_price = product_item.selected_or_first_available_variant.compare_at_price | minus: product_item.selected_or_first_available_variant.price %}
Save {{ saveing_price | money }}
{% endif %}
</span>
</legend>
{% endfor %}
</div>
<div class="product-form-list">
{% assign section_id = section.id | append: '-' | append: block.id %}
{% for product in product.metafields.custom.selling_plan_products.value %}
<product-form
data-product-id="{{ product.id }}"
class="product-form {% if forloop.first %}show{% endif %}"
data-hide-errors="{{ gift_card_recipient_feature_active }}"
data-section-id="{{ section.id }}"
>
<div class="product-form__error-message-wrapper" role="alert" hidden>
<span class="svg-wrapper">
{{- 'icon-error.svg' | inline_asset_content -}}
</span>
<span class="product-form__error-message"></span>
</div>
{%- form 'product',
product,
id: product_form_id,
class: 'form',
novalidate: 'novalidate',
data-type: 'add-to-cart-form'
-%}
{% render 'selling-plans-integration', product: product %}
<input
type="hidden"
name="id"
value="{{ product.selected_or_first_available_variant.id }}"
{% if product.selected_or_first_available_variant.available == false
or quantity_rule_soldout
or product.selected_or_first_available_variant == null
%}
disabled
{% endif %}
class="product-variant-id"
>
<div class="product-form__buttons">
{%- liquid
assign check_against_inventory = true
if product.selected_or_first_available_variant.inventory_management != 'shopify' or product.selected_or_first_available_variant.inventory_policy == 'continue'
assign check_against_inventory = false
endif
if product.selected_or_first_available_variant.quantity_rule.min > product.selected_or_first_available_variant.inventory_quantity and check_against_inventory
assign quantity_rule_soldout = true
endif
-%}
<button
id="ProductSubmitButton-{{ section_id }}"
type="submit"
name="add"
class="product-form__submit button button--full-width {% if show_dynamic_checkout %}button--secondary{% else %}button--primary{% endif %}"
{% if product.selected_or_first_available_variant.available == false
or quantity_rule_soldout
or product.selected_or_first_available_variant == null
%}
disabled
{% endif %}
>
<span>
{%- if product.selected_or_first_available_variant == null -%}
{{ 'products.product.unavailable' | t }}
{%- elsif product.selected_or_first_available_variant.available == false or quantity_rule_soldout -%}
{{ 'products.product.sold_out' | t }}
{%- else -%}
{{ 'products.product.add_to_cart' | t }}
{%- endif -%}
</span>
{%- render 'loading-spinner' -%}
</button>
</div>
{%- endform -%}
</product-form>
{% endfor %}
</div>
{% else %}
<product-form
class="product-form"
data-hide-errors="{{ gift_card_recipient_feature_active }}"
data-section-id="{{ section.id }}"
>
<div class="product-form__error-message-wrapper" role="alert" hidden>
<span class="svg-wrapper">
{{- 'icon-error.svg' | inline_asset_content -}}
</span>
<span class="product-form__error-message"></span>
</div>
{%- form 'product',
product,
id: product_form_id,
class: 'form',
novalidate: 'novalidate',
data-type: 'add-to-cart-form'
-%}
<input
type="hidden"
name="id"
value="{{ product.selected_or_first_available_variant.id }}"
{% if product.selected_or_first_available_variant.available == false
or quantity_rule_soldout
or product.selected_or_first_available_variant == null
%}
disabled
{% endif %}
class="product-variant-id"
>
{%- if gift_card_recipient_feature_active -%}
{%- render 'gift-card-recipient-form', product: product, form: form, section: section -%}
{%- endif -%}
<div class="product-form__buttons">
{%- liquid
assign check_against_inventory = true
if product.selected_or_first_available_variant.inventory_management != 'shopify' or product.selected_or_first_available_variant.inventory_policy == 'continue'
assign check_against_inventory = false
endif
if product.selected_or_first_available_variant.quantity_rule.min > product.selected_or_first_available_variant.inventory_quantity and check_against_inventory
assign quantity_rule_soldout = true
endif
-%}
<button
id="ProductSubmitButton-{{ section_id }}"
type="submit"
name="add"
class="product-form__submit button button--full-width"
{% if product.selected_or_first_available_variant.available == false
or quantity_rule_soldout
or product.selected_or_first_available_variant == null
%}
disabled
{% endif %}
>
<span>
{%- if product.selected_or_first_available_variant == null -%}
{{ 'products.product.unavailable' | t }}
{%- elsif product.selected_or_first_available_variant.available == false or quantity_rule_soldout -%}
{{ 'products.product.sold_out' | t }}
{%- else -%}
{{ 'products.product.add_to_cart' | t }}
{%- endif -%}
</span>
{%- render 'loading-spinner' -%}
</button>
</div>
{%- endform -%}
</product-form>
{% endif %}
{%- else -%}
<div class="product-form">
<div class="product-form__buttons form">
<button
type="submit"
name="add"
class="product-form__submit button button--full-width button--primary"
disabled
>
{{ 'products.product.sold_out' | t }}
</button>
</div>
</div>
{%- endif -%}
</div>
<script src="{{ 'selling-plan.js' | asset_url }}" defer="defer"></script>
main-product.js
最后需要隐藏原生的数量框和价格框