тут очень много с карточкой товара и импортом.

This commit is contained in:
Alisa
2026-06-05 18:50:38 +03:00
parent be883c5843
commit c08519263c
8 changed files with 379 additions and 69 deletions
+8 -1
View File
@@ -62,7 +62,14 @@ $mainImage = $product['main_image_path'] ?? '';
<?php endif; ?>
<span data-dynamic-package-price data-base-package-price="<?= e((string) $variant['package_price']) ?>"><?= e(money_label($variant['package_price'])) ?></span>
</b>
<button class="add-to-cart-button" type="button">В корзину</button>
<div class="product-card-actions">
<div class="quantity-stepper" data-quantity-control aria-label="Количество фасовок">
<button type="button" data-quantity-minus aria-label="Уменьшить количество">-</button>
<input data-quantity-input type="text" inputmode="numeric" value="1" aria-label="Количество">
<button type="button" data-quantity-plus aria-label="Увеличить количество">+</button>
</div>
<button class="add-to-cart-button" type="button">В корзину</button>
</div>
</div>
<?php endforeach; ?>
</section>
+71 -14
View File
@@ -266,8 +266,7 @@ try {
const tierValues = (base) => ({
base: base,
cash20: base * 0.95,
cash50: base * 0.93,
invoice: base * 1.08
cash50: base * 0.93
});
const updateDynamicPrices = (scope) => {
@@ -287,7 +286,7 @@ try {
};
const updateCardPrices = (card) => {
const packageNode = card.querySelector('[data-package-price]');
const packageNode = card.querySelector('[data-dynamic-package-price]');
updateDynamicPrices(card);
if (packageNode) {
@@ -297,12 +296,10 @@ try {
const tierBase = card.querySelector('[data-tier-base]');
const tierCash20 = card.querySelector('[data-tier-cash-20]');
const tierCash50 = card.querySelector('[data-tier-cash-50]');
const tierInvoice = card.querySelector('[data-tier-invoice]');
if (tierBase) tierBase.textContent = formatRub(tiers.base);
if (tierCash20) tierCash20.textContent = formatRub(tiers.cash20);
if (tierCash50) tierCash50.textContent = formatRub(tiers.cash50);
if (tierInvoice) tierInvoice.textContent = formatRub(tiers.invoice);
}
};
@@ -378,6 +375,8 @@ try {
const updateModeButtons = () => {
const payment = getPaymentMode();
document.body.dataset.paymentMode = payment;
document.querySelectorAll('[data-payment-mode]').forEach((button) => {
button.classList.toggle('is-active', button.dataset.paymentMode === payment);
});
@@ -425,6 +424,37 @@ try {
window.RybStockPricing.setPaymentMode(modeButton.dataset.paymentMode || 'cash');
});
document.addEventListener('click', (event) => {
const quantityButton = event.target.closest('[data-quantity-minus], [data-quantity-plus]');
if (!quantityButton) {
return;
}
const control = quantityButton.closest('[data-quantity-control]');
const input = control?.querySelector('[data-quantity-input]');
if (!input) {
return;
}
const current = Math.max(1, parseInt(input.value || '1', 10) || 1);
const next = quantityButton.matches('[data-quantity-plus]')
? current + 1
: Math.max(1, current - 1);
input.value = String(next);
});
document.addEventListener('input', (event) => {
const input = event.target.closest('[data-quantity-input]');
if (!input) {
return;
}
input.value = String(Math.max(1, parseInt(input.value.replace(/\D/g, '') || '1', 10) || 1));
});
document.addEventListener('click', (event) => {
const addButton = event.target.closest('.add-to-cart-button');
@@ -435,19 +465,21 @@ try {
const productScope = addButton.closest('.price-aware');
const row = addButton.closest('.variant-row');
const variantButton = productScope?.querySelector('.variant-choice-button.is-active');
const packagePriceNode = row?.querySelector('[data-dynamic-package-price]') || productScope?.querySelector('[data-package-price]');
const packagePriceNode = row?.querySelector('[data-dynamic-package-price]') || productScope?.querySelector('[data-dynamic-package-price]');
const quantityInput = row?.querySelector('[data-quantity-input]') || productScope?.querySelector('[data-quantity-input]');
if (!productScope || !packagePriceNode) {
return;
}
const addQuantity = Math.max(1, parseInt(quantityInput?.value || '1', 10) || 1);
const variantId = row?.dataset.variantId || variantButton?.dataset.variantId || 'default';
const key = String(productScope.dataset.productId || '') + ':' + variantId;
const items = getCartItems();
const existing = items.find((item) => item.key === key);
if (existing) {
existing.quantity = Number(existing.quantity || 0) + 1;
existing.quantity = Number(existing.quantity || 0) + addQuantity;
} else {
items.push({
key,
@@ -459,7 +491,7 @@ try {
variantName: row?.dataset.variantName || variantButton?.dataset.variantName || 'Фасовка',
basePackagePrice: Number(packagePriceNode.dataset.basePackagePrice || 0),
baseUnitPrice: Number(row?.dataset.baseUnitPrice || variantButton?.dataset.baseUnitPrice || 0),
quantity: 1
quantity: addQuantity
});
}
@@ -526,15 +558,23 @@ try {
});
const unitNode = card.querySelector('[data-unit-label]');
const unitPriceNode = card.querySelector('[data-price-unit]');
const packagePriceNode = card.querySelector('[data-package-price]');
const oldPriceNode = card.querySelector('[data-old-price]');
const unitPriceNode = card.querySelector('[data-dynamic-unit-price]');
const packagePriceNode = card.querySelector('[data-dynamic-package-price]');
const packageLabelNode = card.querySelector('[data-package-label-display]');
const packageLabelNoteNode = card.querySelector('[data-package-label-note]');
const unitLabelNoteNode = card.querySelector('[data-unit-label-note]');
const oldPriceNode = card.querySelector('[data-old-package-price-display]');
const oldUnitPriceNode = card.querySelector('[data-old-unit-price-display]');
if (unitNode) {
unitNode.textContent = button.dataset.unit || 'ед.';
}
if (unitPriceNode && button.dataset.priceUnit) {
if (unitLabelNoteNode) {
unitLabelNoteNode.textContent = button.dataset.unit || 'ед.';
}
if (unitPriceNode) {
unitPriceNode.dataset.baseUnitPrice = button.dataset.baseUnitPrice || '';
}
@@ -542,9 +582,26 @@ try {
packagePriceNode.dataset.basePackagePrice = button.dataset.basePackagePrice || '';
}
if (packageLabelNode) {
packageLabelNode.textContent = button.dataset.variantPackageLabel || button.dataset.variantName || 'Фасовка';
}
if (packageLabelNoteNode) {
packageLabelNoteNode.textContent = button.dataset.variantPackageLabel || button.dataset.variantName || 'Фасовка';
}
if (oldPriceNode) {
oldPriceNode.textContent = button.dataset.oldPrice || '';
oldPriceNode.hidden = !button.dataset.oldPrice;
const oldPackagePrice = button.dataset.oldPrice || '';
oldPriceNode.textContent = oldPackagePrice;
oldPriceNode.hidden = oldPackagePrice === '';
}
if (oldUnitPriceNode) {
const oldUnitPrice = Number(button.dataset.oldUnitPrice || 0);
const baseUnitPrice = Number(button.dataset.baseUnitPrice || 0);
const hasOldUnitPrice = oldUnitPrice > baseUnitPrice;
oldUnitPriceNode.textContent = hasOldUnitPrice ? formatRub(oldUnitPrice) : '';
oldUnitPriceNode.hidden = !hasOldUnitPrice;
}
updateCardPrices(card);
+81 -27
View File
@@ -9,11 +9,26 @@ $activeVariant = $variants[0] ?? [
'unit' => $product['unit'] ?? '',
'price_per_unit' => $product['price_per_unit'] ?? null,
'package_price' => $product['package_price'] ?? null,
'package_quantity' => $product['package_quantity'] ?? 0,
'old_package_price' => $product['old_package_price'] ?? null,
];
$cardId = 'product-card-' . (int) ($product['id'] ?? 0);
$activePackagePrice = (float) ($activeVariant['package_price'] ?? 0);
$activeUnitPrice = $activeVariant['price_per_unit'] === null ? null : (float) $activeVariant['price_per_unit'];
$activePackageQuantity = (float) ($activeVariant['package_quantity'] ?? 0);
$activeVariantOldPackagePrice = $activeVariant['old_package_price'] ?? null;
$activeOldPackagePrice = (
$activeVariantOldPackagePrice !== null
&& (float) $activeVariantOldPackagePrice > $activePackagePrice
)
? (float) $activeVariantOldPackagePrice
: null;
$activeOldUnitPrice = ($activeOldPackagePrice !== null && $activePackageQuantity > 0 && $activeUnitPrice !== null)
? $activeOldPackagePrice / $activePackageQuantity
: null;
if ($activeOldUnitPrice !== null && $activeOldUnitPrice <= $activeUnitPrice) {
$activeOldUnitPrice = null;
}
?>
<article
class="product-card price-aware"
@@ -27,38 +42,37 @@ $activeUnitPrice = $activeVariant['price_per_unit'] === null ? null : (float) $a
<a class="product-card-image" href="/product/<?= e($product['slug']) ?>" aria-label="<?= e($product['name']) ?>">
<img src="<?= e((string) $product['main_image_path']) ?>" alt="<?= e((string) $product['name']) ?>">
</a>
<?php else: ?>
<a class="product-card-image product-card-image-placeholder" href="/product/<?= e($product['slug']) ?>" aria-label="<?= e($product['name']) ?>">
<span>Рыбсток</span>
</a>
<?php endif; ?>
<p class="product-category"><?= e($product['category_display_name'] ?? $product['category_name'] ?? 'Каталог') ?></p>
<h3><a href="/product/<?= e($product['slug']) ?>"><?= e($product['name']) ?></a></h3>
<?php if ($activeVariant['price_per_unit'] !== null): ?>
<span class="price-line">Цена за <span data-unit-label><?= e(unit_label((string) $activeVariant['unit'])) ?></span>: <b data-price-unit data-dynamic-unit-price data-base-unit-price="<?= e((string) $activeUnitPrice) ?>"><?= e(money_label($activeVariant['price_per_unit'])) ?></b></span>
<?php endif; ?>
<?php if ($activeVariant['package_price'] !== null): ?>
<strong class="package-price-line">
<span class="old-price" data-old-price<?= empty($activeVariant['old_package_price']) ? ' hidden' : '' ?>><?= e(money_label($activeVariant['old_package_price'] ?? null)) ?></span>
За упаковку: <span data-package-price data-dynamic-package-price data-base-package-price="<?= e((string) $activePackagePrice) ?>"><?= e(money_label($activeVariant['package_price'])) ?></span>
<button class="price-tier-trigger" type="button" aria-label="Показать градацию цены">?</button>
</strong>
<?php endif; ?>
<div class="price-tier-popover" role="tooltip">
<strong>Варианты цены</strong>
<dl>
<div><dt>Базовая цена</dt><dd data-tier-base><?= e(money_label($activePackagePrice)) ?></dd></div>
<div><dt>Цена при сумме заказа от 20 000р. наличными</dt><dd data-tier-cash-20><?= e(money_label(floor($activePackagePrice * 0.95))) ?></dd></div>
<div><dt>Цена при сумме заказа от 50 000р. наличными</dt><dd data-tier-cash-50><?= e(money_label(floor($activePackagePrice * 0.93))) ?></dd></div>
<div><dt>Оплата по расчетному счету</dt><dd data-tier-invoice><?= e(money_label(floor($activePackagePrice * 1.08))) ?></dd></div>
</dl>
</div>
<?php if ($variants !== []): ?>
<div class="variant-choice" aria-label="Выбор фасовки">
<span>Фасовка:</span>
<span>Доступные фасовки:</span>
<div class="variant-choice-list">
<?php foreach ($variants as $index => $variant): ?>
<?php
$variantPackagePrice = (float) ($variant['package_price'] ?? 0);
$variantUnitPrice = $variant['price_per_unit'] === null ? null : (float) $variant['price_per_unit'];
$variantPackageQuantity = (float) ($variant['package_quantity'] ?? 0);
$variantOldPackagePrice = (
!empty($variant['old_package_price'])
&& (float) $variant['old_package_price'] > $variantPackagePrice
)
? (float) $variant['old_package_price']
: null;
$variantOldUnitPrice = ($variantOldPackagePrice !== null && $variantPackageQuantity > 0 && $variantUnitPrice !== null)
? $variantOldPackagePrice / $variantPackageQuantity
: null;
if ($variantOldUnitPrice !== null && $variantOldUnitPrice <= $variantUnitPrice) {
$variantOldUnitPrice = null;
}
?>
<button
class="variant-choice-button<?= $index === 0 ? ' is-active' : '' ?>"
type="button"
@@ -67,10 +81,12 @@ $activeUnitPrice = $activeVariant['price_per_unit'] === null ? null : (float) $a
data-variant-name="<?= e((string) ($variant['name'] ?? '')) ?>"
data-unit="<?= e(unit_label((string) $variant['unit'])) ?>"
data-base-unit-price="<?= e((string) ($variant['price_per_unit'] ?? '')) ?>"
data-old-unit-price="<?= e($variantOldUnitPrice === null ? '' : (string) $variantOldUnitPrice) ?>"
data-base-package-price="<?= e((string) ($variant['package_price'] ?? '')) ?>"
data-price-unit="<?= e(money_label($variant['price_per_unit'])) ?>"
data-package-price="<?= e(money_label($variant['package_price'])) ?>"
data-old-price="<?= e(money_label($variant['old_package_price'] ?? null)) ?>"
data-variant-package-label="<?= e((string) ($variant['name'] ?? '')) ?>"
data-variant-unit-price-label="<?= e(money_label($variant['price_per_unit'])) ?>"
data-variant-package-price-label="<?= e(money_label($variant['package_price'])) ?>"
data-old-price="<?= e($variantOldPackagePrice === null ? '' : money_label($variantOldPackagePrice)) ?>"
>
<?= e((string) $variant['name']) ?>
</button>
@@ -79,5 +95,43 @@ $activeUnitPrice = $activeVariant['price_per_unit'] === null ? null : (float) $a
</div>
<?php endif; ?>
<button class="add-to-cart-button" type="button">В корзину</button>
<?php if ($activeVariant['price_per_unit'] !== null): ?>
<span class="price-line">
Цена за <span data-unit-label><?= e(unit_label((string) $activeVariant['unit'])) ?></span>:
<span class="old-price" data-old-unit-price-display<?= $activeOldUnitPrice === null ? ' hidden' : '' ?>><?= e(money_label($activeOldUnitPrice)) ?></span>
<b data-price-unit data-dynamic-unit-price data-base-unit-price="<?= e((string) $activeUnitPrice) ?>"><?= e(money_label($activeVariant['price_per_unit'])) ?></b>
</span>
<?php endif; ?>
<?php if ($activeVariant['package_price'] !== null): ?>
<strong class="package-price-line">
<span class="package-price-label">За фасовку <span data-package-label-display><?= e((string) $activeVariant['name']) ?></span>:</span>
<span class="package-price-values">
<span class="old-price" data-old-package-price-display<?= $activeOldPackagePrice === null ? ' hidden' : '' ?>><?= e(money_label($activeOldPackagePrice)) ?></span>
<span data-package-price data-dynamic-package-price data-base-package-price="<?= e((string) $activePackagePrice) ?>"><?= e(money_label($activeVariant['package_price'])) ?></span>
</span>
<button class="price-tier-trigger" type="button" aria-label="Показать градацию цены">?</button>
</strong>
<?php if ($activeVariant['price_per_unit'] !== null): ?>
<span class="package-price-note">Цена за <span data-unit-label-note><?= e(unit_label((string) $activeVariant['unit'])) ?></span> × <span data-package-label-note><?= e((string) $activeVariant['name']) ?></span></span>
<?php endif; ?>
<?php endif; ?>
<div class="price-tier-popover" role="tooltip">
<strong>Варианты цены за наличный расчет:</strong>
<dl>
<div><dt>Базовая цена</dt><dd data-tier-base><?= e(money_label($activePackagePrice)) ?></dd></div>
<div><dt>При заказе от 20 000р.</dt><dd data-tier-cash-20><?= e(money_label(floor($activePackagePrice * 0.95))) ?></dd></div>
<div><dt>При заказе от 50 000р.</dt><dd data-tier-cash-50><?= e(money_label(floor($activePackagePrice * 0.93))) ?></dd></div>
</dl>
</div>
<div class="product-card-actions">
<div class="quantity-stepper" data-quantity-control aria-label="Количество фасовок">
<button type="button" data-quantity-minus aria-label="Уменьшить количество">-</button>
<input data-quantity-input type="text" inputmode="numeric" value="1" aria-label="Количество">
<button type="button" data-quantity-plus aria-label="Увеличить количество">+</button>
</div>
<button class="add-to-cart-button" type="button">В корзину</button>
</div>
</article>