UNPKG

zz-shopify-components

Version:

Reusable Shopify components for theme projects

402 lines (355 loc) 11.8 kB
class BuyNowBottomBar extends HTMLElement { loading = false; bindProducts = []; mainProductId = ''; currency = ''; showMore = false; isEduProduct = false; isLoggedIn = false; isEduVerified = false; eduPageUrl = ''; constructor() { super(); this.cart = document.querySelector('cart-notification') || document.querySelector('cart-drawer'); this.careGuide = this.dataset.careGuide; this.hasCare = this.dataset.hasCare || false; this.init(); this.currency = this.dataset.currency; this.isocode = this.dataset.isocode; this.isEduProduct = this.dataset.isEduProduct === 'true'; this.isLoggedIn = this.dataset.isLoggedIn === 'true'; this.isEduVerified = this.dataset.isEduVerified === 'true'; this.eduPageUrl = this.dataset.eduPageUrl; } init() { this.querySelector('button').addEventListener('click', async () => { this.handleToPay(); }); if (this.querySelector('.show-more-btn')) { this.querySelector('.show-more-btn').addEventListener( 'click', async () => { this.toggleShowMoreDesc(); } ); } window.addEventListener('pageshow', () => { this.updatePrice(); }); } handleAddToCart() { this.getMainProduct(); this.getBindProduct(); this.onSubmitHandler(); } showCareChooseDialog() { const careDialog = document.querySelector( 'product-detail-dialog-hovercare-choose' ); if (careDialog) { careDialog.showModal(); } } handleToPay() { this.getMainProduct(); const cartUrl = `/cart/${this.mainProductId}:${this.mainProductQuantity}?checkout&currency=${this.isocode}`; window.location.href = cartUrl; } async onSubmitHandler() { const data = { items: [ { id: this.mainProductId, quantity: 1, }, ...this.bindProducts, ], sections: 'cart-drawer,cart-icon-bubble', }; this.toggleLoading(); await fetch(`${routes.cart_add_url}`, { method: 'POST', headers: { 'Content-Type': 'application/json', Accept: 'application/javascript', }, body: JSON.stringify(data), }) .then((response) => response.json()) .then((response) => { if (response.status) { publish(PUB_SUB_EVENTS.cartError, { source: 'product-form', productVariantId: this.mainProductId, errors: response.errors || response.description, message: response.message, }); } else if (!this.cart) { window.location = window.routes.cart_url; return; } if (!this.error) publish(PUB_SUB_EVENTS.cartUpdate, { source: 'product-form', productVariantId: this.mainProductId, cartData: response, }); this.error = false; const quickAddModal = this.closest('quick-add-modal'); if (quickAddModal) { document.body.addEventListener( 'modalClosed', () => { setTimeout(() => { this.cart.renderContents(response); }); }, { once: true } ); quickAddModal.hide(true); } else { this.cart.renderContents(response); } }) .catch((e) => { console.error(e); }) .finally(() => { if (this.cart && this.cart.classList.contains('is-empty')) this.cart.classList.remove('is-empty'); this.toggleLoading(); }); } getMainProduct() { this.mainProductId = document.querySelector( 'product-buy-now-selector' ).currentVariantId; this.mainProductQuantity = document.querySelector( 'product-buy-now-counter input' ).value; } getBindProduct() { this.bindProducts = []; const tags = Array.from(document.querySelectorAll('accessory-product')); tags.forEach((item) => { if (item.isSelected) { this.bindProducts.push({ id: item.currentId, quantity: item.getNumber(), }); } }); const careTags = Array.from(document.querySelectorAll('care-product')); if (careTags && careTags.length) { careTags.forEach((item) => { if (item.isShow && item.isSelected) { this.bindProducts.push({ id: item.currentId, quantity: 1, }); } }); } const careDialog = document.querySelector( 'product-detail-dialog-hovercare-choose' ); if (careDialog && careDialog.chooseId) { this.bindProducts.push({ id: careDialog.chooseId, quantity: 1, }); } } toggleLoading() { this.loading = !this.loading; this.querySelector('.buy-loading').classList.toggle('tw-hidden'); } toggleShowMoreDesc() { const tipEl = this.querySelector('.bar-tip'); const svg = this.querySelector('.show-more-btn'); this.showMore = !this.showMore; gsap.to(svg, { rotation: this.showMore ? 180 : 0, duration: 0.3, }); tipEl.classList.toggle('show-more-desc', this.showMore); } // available: "true" or "false" toggleAddToCartButton(available) { const button = this.querySelector('button'); const btnText = this.querySelector('.buy-now-bottom-bar-btn-text'); if (!button) { return; } if (button.disabled && available === 'true') { button.disabled = false; btnText.textContent = this.dataset.btnText; } else if (!button.disabled && available === 'false') { button.disabled = true; btnText.textContent = this.dataset.btnOutOfStockText; } } updatePrice() { if (!window.Decimal) { return; } console.log('this.isocode', this.isocode); console.log('this.currency', this.currency); const main = document .querySelector('.product-version-option input:checked') ?.closest('.product-version-option'); const mainProductQuantity = document.querySelector( 'product-buy-now-counter input' ).value; const { price: mainPrice, before: mainBefore, available: mainAvailable, } = main.dataset; let total_price = new Decimal(this.handlePrice(mainPrice)); let total_before = new Decimal(this.handlePrice(mainBefore)); total_price = total_price.times(mainProductQuantity); total_before = total_before.times(mainProductQuantity); console.log('total_price', total_price); console.log('total_before', total_before); this.toggleAddToCartButton(mainAvailable); // 找到被选中的附加产品 const selectedAccessories = Array.from( document.querySelectorAll('accessory-product') ).filter((item) => item.isSelected); for (const item of selectedAccessories) { const count = parseInt(item.getNumber(), 10); const priceEl = item.querySelector('.current-price'); if (!priceEl) { console.warn('找不到 .current-price 元素'); continue; } const { price, before } = priceEl.dataset; const currentPrice = new Decimal(this.handlePrice(price)); const originPrice = new Decimal(this.handlePrice(before)); total_price = total_price.plus(currentPrice.times(count)); const actualBefore = currentPrice.greaterThan(originPrice) ? currentPrice.times(count) : originPrice.times(count); total_before = total_before.plus(actualBefore); } // 找到care 商品 const bindCares = Array.from(document.querySelectorAll('care-product')); bindCares.forEach((item) => { const count = parseInt(item.getNumber(), 10) || 0; // 确保 count 是有效数字 if (item.isShow && item.isSelected) { const priceEl = item.querySelector('.current-price'); if (priceEl) { const { price, before } = priceEl.dataset; const currentPrice = new Decimal(this.handlePrice(price)); const originPrice = new Decimal(this.handlePrice(before)); total_price = total_price.plus(currentPrice.times(count)); const actualBefore = currentPrice.lessThan(originPrice) ? originPrice.times(count) : currentPrice.times(count); total_before = total_before.plus(actualBefore); } else { console.warn('找不到 .current-price 元素'); } } }); // 计算加个 const priceText = this.currency + total_price.toString(); const beforeText = this.currency + total_before.toString(); const showBefore = total_before.greaterThan(total_price); requestAnimationFrame(() => { const priceEl = this.querySelector('.price'); if (priceEl) { priceEl.textContent = Shopify.formatMoneyFromDecimal(total_price); } const beforeEl = this.querySelector('.before'); if (beforeEl) { beforeEl.textContent = Shopify.formatMoneyFromDecimal(total_before); } const mbPriceEl = this.querySelector('.mb_price'); if (mbPriceEl) { mbPriceEl.textContent = Shopify.formatMoneyFromDecimal(total_price); } const mbBeforeEl = this.querySelector('.mb_before'); if (mbBeforeEl) { mbBeforeEl.textContent = Shopify.formatMoneyFromDecimal(total_before); } // 计算折扣 const discountEl = this.querySelectorAll('.price-discouter'); if (discountEl.length > 0) { const percent = total_price.times(100).dividedBy(total_before); const discountPercent = 100 - percent; const discountValue = total_before.minus(total_price); discountEl.forEach((el) => { if (total_before.greaterThan(total_price)) { el.classList.remove('tw-hidden'); el.textContent = this.dataset.discountType === 'percent' ? '- ' + Math.round(discountPercent) + '%' : 'Save ' + this.currency + discountValue; } else { el.classList.add('tw-hidden'); } }); } }); // const toggleClass = (el, show) => { // if (!el) return; // el.classList.toggle('tw-hidden', !show); // }; // toggleClass(beforeEl, showBefore); // toggleClass(mbBeforeEl, showBefore); this.updatePayPal(total_price.toString()); } handlePrice(price) { console.log('handlePrice', price); if (!price) return 0; if (this.currency === '€') { return this.handleEuroPrice(price); } return price.toString().replace(/,/g, '') || 0; } handleEuroPrice(price) { let result = price.toString(); const priceFormat = this.dataset.priceFormat || ''; if (priceFormat.includes('amount_with_comma_separator')) { result = result.replace('.', ''); result = result.replace(',', '.'); } else { result = result.replace(/,/g, ''); } return result || 0; } updatePayPal(price) { if (!price) return; clearTimeout(this._paypalTimer); const paypalEls = document.querySelectorAll('.product-buy-paypal'); if (!this.paypalLoading) { this.paypalLoading = true; paypalEls.forEach((el) => { el.classList.add('tw-daisy-loading'); }); } paypalEls.forEach((el) => { el.dataset.ppAmount = price; }); this._paypalTimer = setTimeout(() => { paypalEls.forEach((el) => { el.classList.remove('tw-daisy-loading'); }); this.paypalLoading = false; }, 800); } // 有预期到货时间不展示 toggleShowTitleDesc(available) { const barTitleDesc = this.querySelector('.bar-title-desc'); if (!available) { barTitleDesc.classList.add('tw-hidden'); } else { barTitleDesc.classList.remove('tw-hidden'); } } } customElements.define('buy-now-bottom-bar', BuyNowBottomBar);