PageSourceSearch

https://www.yalamps.com/dist/front/static/new_default_theme/cart.js?v=630

js yalamps.com collected 2026-10-06 13:38:25 UTC 39,777 bytes, 909 lines download raw bytes

1// 购物车侧边栏自定义元素(滑出式购物车)
2if (!customElements.get('f-cart-drawer')) {
3    class FCartDrawer extends HTMLElement {
4        cartUpdateUnsubscriber = undefined; // 购物车更新取消订阅函数
5
6        constructor() {
7            super();
8            // 绑定事件监听器以保持正确的this上下文
9            this.getSectionToRenderListener = this.getSectionToRender.bind(this);
10            // 先取消上一次的订阅(如果存在)
11            if (this.cartUpdateUnsubscriber && typeof this.cartUpdateUnsubscriber === 'function') {
12                this.cartUpdateUnsubscriber();
13            }
14
15            // 订阅购物车更新事件
16            this.cartUpdateUnsubscriber = FecEvents.on(FecTheme.pubSubEvents.cartDrawerRefresh, this.getDrawerData.bind(this));
17
18        }
19
20        // 获取用于AJAX购物车更新的区块名称
21        get sectionName() {
22            return 'cart-drawer';
23        }
24
25        // 获取购物车侧边栏内部元素
26        get cartDrawerInner() {
27            return this.querySelector(".f-cart-drawer__inner");
28        }
29
30        // 获取关闭图标元素
31        get cartDrawerCloseIcon() {
32            return this.querySelector(".f-cart-drawer__close");
33        }
34
35        getDrawerData() {//获取购物车数据
36            api.get('/api/checkout/cart/get-cart-info', {
37                format: 'sidebar_html',//侧栏购物车 sidebar_html 购物车页面 page_html
38                page_type: window.baseData.page_type
39            }).then(res => {
40                $log('getDrawerData-购物车数据', res)
41                // 将购物车数据放到 html 里方便取值
42                window.$updateCartItemsData(res.items)
43                this.cartUpdateUnsubscriber();//一定要取消订阅,防止 html 渲染时,重复监听
44                if (res.payment_new_paypal) {
45                    sessionStorage.setItem("payment_new_paypal", JSON.stringify(res.payment_new_paypal))
46                } else {
47                    sessionStorage.removeItem("payment_new_paypal");
48                }
49                //将购物车完整的 html 插入到 page-right-cart-drawer 这个 class里
50                if (res.payment_paypal && res.payment_paypal.hasOwnProperty('commit')) {
51                    sessionStorage.setItem('paypal_info', JSON.stringify(res.payment_paypal));//PayPal 信息存起来
52                } else {
53                    sessionStorage.removeItem('paypal_info')
54                }
55                let cart_qty = res.count
56                FecEvents.emit(FecTheme.pubSubEvents.cartUpdate, {cart_qty: cart_qty});//更新顶部购物车数量
57
58                let cartDrawer = document.querySelector('.page-right-cart-drawer')
59                if (cartDrawer) {
60                    let isOpen = cartDrawer.querySelector('.f-cart-drawer--active')
61                    if (isOpen) {//已经打开窗口了,用 js 替换显示总金额即可
62                        // 1️ 解析返回的 HTML,建立临时 DOM 容器
63                        let tempDiv = document.createElement('div');
64                        tempDiv.innerHTML = res.html;
65
66                        // 定义需要替换的标签选择器(可扩展)
67                        const targets = [
68                            '.f-cart__subtotal--price',
69                            '.f-cart-drawer__body'
70                        ];
71
72                        // 2️ 循环替换
73                        targets.forEach(selector => {
74                            let newEl = tempDiv.querySelector(selector);
75                            let oldEl = isOpen.querySelector(selector);
76
77                            if (newEl && oldEl) {
78                                oldEl.outerHTML = newEl.outerHTML;
79                                $log(` 已替换 ${selector}`);
80                            } else {
81                                $log(`未找到 ${selector} 元素`, {newEl, oldEl});
82                            }
83                        });
84
85                    } else {//没有打开窗口,可以塞入
86                        cartDrawer.innerHTML = res.html;
87                    }
88                }
89
90            })
91        }
92
93
94        // 将当前区块添加到需要渲染的区块列表中
95        getSectionToRender(event) {
96            event.detail.sections.push(this.sectionName);
97        }
98
99        // 元素插入DOM时调用
100        connectedCallback() {
101            // 监听购物车区块更新事件
102            document.addEventListener('cart:grouped-sections', this.get
102SectionToRenderListener);
103
104            // 点击事件处理:点击外部或关闭按钮时关闭侧边栏
105            this.addEventListener("click", (event) => {
106                if (event.target.closest(".f-cart-drawer__inner") !== this.cartDrawerInner || event.target === this.cartDrawerCloseIcon) {
107                    this.close();
108                }
109            });
110        }
111
112        // 元素从DOM移除时调用
113        disconnectedCallback() {
114            document.removeEventListener('cart:grouped-sections', this.getSectionToRenderListener);
115            if (this.cartUpdateUnsubscriber) {
116                this.cartUpdateUnsubscriber();
117            }
118        }
119
120        // 打开购物车侧边栏
121        open(triggeredBy) {
122            $log('购物车打开函数', triggeredBy);
123            if (triggeredBy) this.setActiveElement(triggeredBy); // 设置触发元素
124            this.classList.add("f-cart-drawer--active"); // 添加激活类
125            setTimeout(() => {
126                this.classList.add("f-cart-drawer__inner-show"); // 添加激活类
127            }, 200)
128            // document.documentElement.classList.add("prevent-scroll"); // 防止页面滚动
129        }
130
131        // 关闭购物车侧边栏
132        close() {
133            this.classList.remove("f-cart-drawer__inner-show");
134
135            // 延迟移除激活类,确保动画完成
136            setTimeout(() => {
137                this.classList.remove("f-cart-drawer--active");
138                document.documentElement.classList.remove("prevent-scroll");
139            }, 300);
140        }
141
142        // 设置当前活动元素
143        setActiveElement(element) {
144            this.activeElement = element;
145        }
146    }
147
148    customElements.define("f-cart-drawer", FCartDrawer);
149
150}
151
152// 注册自定义元素
153
154// 购物车删除按钮自定义元素
155if (!customElements.get('f-cart-remove-button')) {
156    class FCartRemoveButton extends HTMLElement {
157        constructor() {
158            super();
159            // 点击事件处理:删除对应商品
160            this.addEventListener("click", (event) => {
161                event.preventDefault();
162                api.post('/api/checkout/cart/delete-cart-item', {cart_item_id: this.dataset.itemId}).then(res => {
163                    if (window.baseData.page_type === 'cart') {
164                        FecEvents.emit(FecTheme.pubSubEvents.cartPageRefresh)//通知购物车页刷新 html 结构
165                    } else {
166                        FecEvents.emit(FecTheme.pubSubEvents.cartDrawerRefresh)//通知抽屉刷新 html 结构
167                    }
168                    this.removeCartEventTracking(this.dataset.itemId)//事件追踪
169                }).catch(err => {
170                    FecTheme.Notification.show({
171                        method: "appendChild",
172                        type: "error",
173                        message: err.message,
174                        duration: 5000,
175                    });
176                });
177            });
178        }
179        removeCartEventTracking(productId){//删除购物车商品事件追踪
180            let cartList = $getCartItemsData()
181            if (!Array.isArray(cartList) || cartList.length === 0) {
182                $log('购物车数据为空');
183                return;
184            }
185            // 根据 productId 查找对应的商品对象
186            const itemProduct = cartList.find(item => String(item.item_id) === String(productId));
187            if (!itemProduct) {
188                return;
189            }
190            let google = document.querySelector('addons-google')
191            if (google) {//google
192                try {
193                    google.removeCartGoogleAnalysis(itemProduct);
194                } catch (e) {
195                    console.error(e);
196                }
197            }
198
199
200        }
201    }
202
203// 注册自定义元素
204    customElements.define("f-cart-remove-button", FCartRemoveButton);
205}
206
207// 购物车模板自定义元素
208if (!customElements.get('f-cart')) {
209    class FCartTemplate extends HTMLElement {
210        constructor() {
211            super();
212            // 监听购物车区块更新事件
213            document.addEventListener('cart:grouped-sections', this.get
213SectionToRender.bind(this));
214            $log('购物车页面加载')
215        }
216
217        // 获取区块名称
218        get sectionName() {
219            return 'cart-template';
220        }
221
222        // 将当前区块添加到需要渲染的区块列表中
223        getSectionToRender(event) {
224            event.detail.sections.push(this.sectionName);
225        }
226    }
227
228// 注册自定义元素
229    customElements.define("f-cart", FCartTemplate);
230}
231
232// 购物车商品列表自定义元素
233if (!customElements.get('f-cart-items')) {
234    class FCartItems extends HTMLElement {
235        cartUpdateUnsubscriber = undefined; // 购物车更新取消订阅函数
236
237        constructor() {
238            super();
239
240            // 设置需要更新的区块名称
241            window.FoxKitSections = [this.sectionName, this.cartCountSectionName];
242            this.rootUrl = '/' // 获取商店根URL
243
244            // 绑定事件监听器
245            this.onCartRefreshListener = this.onCartRefresh.bind(this);
246            document.addEventListener("cart:refresh", this.onCartRefreshListener);
247
248            // 监听数量输入变化(使用防抖函数)
249            this.addEventListener('change', debounce(this.onChange.bind(this), 300));
250
251            // 订阅购物车更新事件
252            this.cartUpdateUnsubscriber = FecEvents.on(FecTheme.pubSubEvents.cartUpdate, this.onCartUpdate.bind(this));
253        }
254
255        // 元素插入DOM时调用
256        connectedCallback() {
257            this.isCartPage = window.baseData.page_type === "cart"; // 判断是否在购物车页面
258            this.cartDrawerInner = document.querySelector(".f-cart-drawer__inner");//侧栏购物车
259
260            // 根据页面类型设置加载指示器目标
261            let loadingTarget = this.cartDrawerInner;
262            if (this.isCartPage) loadingTarget = document.body;
263            this.loading = new FecLibs.AnimateLoading(loadingTarget, {overlay: loadingTarget});
264        }
265
266        // 获取区块名称
267        get sectionName() {
268            return this.dataset.sectionName || 'cart-template';
269        }
270
271        // 获取购物车数量区块名称
272        get cartCountSectionName() {
273            return 'cart-count';
274        }
275
276        // 元素从DOM移除时调用
277        disconnectedCallback() {
278            // 取消订阅和事件监听
279            if (this.cartUpdateUnsubscriber) {
280                this.cartUpdateUnsubscriber();
281            }
282            document.removeEventListener("cart:refresh", this.onCartRefreshListener);
283        }
284
285        // 数量输入变化处理
286        onChange(event) {
287            const {target} = event;
288            $log('输入数量变了', target, target.dataset, target.value)
289            if (target.closest('f-quantity-input')) { // 如果是数量输入框
290                if (Number(target.value) > 0) {
291                    this.updateQuantity(
292                        target.dataset.index, // 商品索引
293                        target.value, // 新数量
294                        document.activeElement.getAttribute('name') || target.name, // 活动元素名称
295                        target // 目标元素
296                    );
297                }
298
299            }
300        }
301
302        // 购物车错误处理
303        onCartError(errors, target, line) {
304            if (target) {
305                // 在数量输入框显示错误
306                const quantityInput = target.closest('f-quantity-input');
307                quantityInput.setValidity(errors);
308            } else {
309                // 重定向到购物车页面
310                window.location.href = MinimogSettings.routes.cart;
311            }
312
313            // 更新实时错误提示区域
314            this.updateLiveRegions(line, errors);
315        }
316
317        // 购物车刷新处理
318        async onCartRefresh(event) {
319            $log('onCartRefresh', event)
320            const cartSelector = this.isCartPage ? 'f-cart' : 'f-cart-drawer';
321            const cartElement = document.querySelector(cartSelector);
322            const cartCount = document.querySelectorAll('f-cart-count');
323
324            try {
325                // 获取最新的购物车数据
326                await fetch(`${this.rootUrl}?sections[]=${this.sectionName}&sections[]=${this.cartCountSectionName}`)
327                    .then((response) => response.text())
328                    .then((responseText) => {
329                        const parseJSON = JSON.parse(responseText);
330
331                        // 更新购物车各个区块
332                        this.getSectionsToRender().forEach((section) => {
333                            const elementToReplace =
334                                document.getElementById(section.id)?.querySelector(section.selector) || document.getElementById(section.id);
335                            if (elementToReplace) elementToReplace.innerHTML = this.getSectionInnerHTML(parseJSON[this.sectionName], section.selector);
336                        });
337
338                        // 更新购物车数量显示
339                        const responseCartCount = parseJSON[this.cartCountSectionName];
340                        const newCartCount = new DOMParser().parseFromString(responseCartCount, "text/html");
341                        cartCount.forEach(cart => cart.onUpdate(newCartCount));
342                    });
343
344                // 如果需要打开侧边栏
345                if (event.detail?.open === true) {
346                    if (!this.isCartPage) cartElement.open();
347                }
348            } catch (error) {
349                console.error("onCartRefresh:", error);
350            }
351        }
352
353        // 购物车更新处理
354        onCartUpdate(event) {
355            $log('onCartUpdate-cart.js', event)
356            // 暂时用不到
357            return
358
359            // 更新购物车各个区块
360            this.getSectionsToRender().forEach((section) => {
361                const elementToReplace = document.getElementById(section.id)?.querySelector(section.selector);
362                if (elementToReplace) elementToReplace.innerHTML = this.getSectionInnerHTML(event.cart.sections[this.sectionName], section.selector);
363            });
364
365            // 触发购物车更新事件
366            document.dispatchEvent(
367                new CustomEvent('cart:updated', {
368                    detail: {
369                        cart: event.cart,
370                    },
371                })
372            );
373        }
374
375        // 更新商品数量
376        updateQuantity(line, quantity, name, target) {
377            $log('更新购物车-updateQuantity', line, quantity, name, target)
378
379            this.loading.show(); // 显示加载指示器
380            let params = {
381                "cart_item_id": Number(target.dataset.itemId),
382                "updated_qty": Number(quantity)
383            }
384            $log('更新购物车-updateQuantity', line, quantity, name, params, target)
385
386            api.post('/api/checkout/cart/update-cart-item-qty', params).then((response) => {
387                // 更新购物车数量 增加购物车数量变化的订阅事件
388                // document.querySelector('.page-right-cart-drawer').innerHTML = response.html;
389                $log('window.baseData.page_type', window.baseData.page_type)
390                if (window.baseData.page_type === 'cart') {
391                    FecEvents.emit(FecTheme.pubSubEvents.cartPageRefresh)//通知购物车页刷新 html 结构
392                } else {
393                    FecEvents.emit(FecTheme.pubSubEvents.cartDrawerRefresh)//通知抽屉刷新 html 结构
394                }
395                if (name === 'plus') {//增加数量
396                    this.sendCardEventTracking(target.dataset.itemId, 1)//购物车事件跟踪
397                } else if (name === 'updates') {//输入框手动输入的值
398                    let oldQuantity = Number(target.dataset.cartQuantity);
399                    let qty = Number(quantity) - oldQuantity;
400                    if (qty > 0) {//计算出加了几个商品
401                        this.sendCardEventTracking(target.dataset.itemId, qty)//购物车事件跟踪
402                    }
403                }
404
405            }).catch((error) => {
406                if (error.code === 101000012) {//限制最小购买数量
407                    let input = this.querySelector(`input.f-quantity__input[data-item-id="${target.dataset.itemId}"]`);
408                    if (input) {//重新调用
409                        this.updateQuantity(line, input.min || 1, name, target)
410                    }
411                }
412                FecTheme.Notification.show({
413                    method: "appendChild",
414                    type: "error",
415                    message: error.message,
416                    duration: 5000,
417                });
418            }).finally(() => {
419                this.loading.hide(); // 隐藏加载指示器
420            })
421
422        }
423
424        // 更新实时错误提示区域
425        updateLiveRegions(line, message) {
426            let lineItemNode = document.getElementById(`MinimogCart-Item-${line}`);
427            if (message !== "" && lineItemNode) {
428                MinimogTheme.Notification.show({
429                    target: lineItemNode,
430                    type: "warning",
431                    message: message,
432                });
433            }
434        }
435
436        // 从HTML字符串中提取指定选择器的内容
437        getSectionInnerHTML(html, selector) {
438            return new DOMParser().parseFromString(html, 'text/html').querySelector(selector).innerHTML;
439        }
440
441        // 获取需要渲染的区块配置
442        getSectionsToRender() {
443            return [
444                {
445                    id: 'FecCartCartDrawer',
446                    selector: '#FecCartDrawerHeader',
447                },
448                // ...其他区块配置
449            ];
450        }
451
452
453        //购物车事件追踪
454        sendCardEventTracking(productId, qty) {
455            let cartList = $getCartItemsData()
456            if (!Array.isArray(cartList) || cartList.length === 0) {
457                $log('购物车数据为空');
458                return;
459            }
460            // 根据 productId 查找对应的商品对象
461            const itemProduct = cartList.find(item => String(item.item_id) === String(productId));
462            if (!itemProduct) {
463                return;
464            }
465            if (itemProduct.product_f_title) itemProduct['product_title'] = itemProduct.product_f_title
466            let facebook = document.querySelector('addons-facebook')
467            if (facebook) {//脸书
468                try {
469                    facebook.addToCardFb(itemProduct, qty);
470                } catch (e) {
471                    console.error(e);
472                }
473            }
474            let tiktok = document.querySelector('addons-tiktok')
475            if (tiktok) {//TikTok
476                try {
477                    tiktok.addToCartTiktok(itemProduct, qty);
478                } catch (e) {
479                    console.error(e);
480                }
481            }
482            let pinterest = document.querySelector('addons-pinterest')
483            if (pinterest) {//pinterest
484                try {
485                    pinterest.sendAddToCartPinterest(itemProduct, qty);
486                } catch (e) {
487                    console.error(e);
488                }
489            }
490            let bing = document.querySelector('addons-bing')
491            if (bing) {//bing
492                try {
493                    bing.addToCartBind(itemProduct, qty);
494                } catch (e) {
495                    console.error(e);
496                }
497            }
498            let google = document.querySelector('addons-google')
499            if (google) {//google
500                try {
501                    google.addToCartGoogleAnalysis(itemProduct, qty);
502                } catch (e) {
503                    console.error(e);
504                }
505            }
506
507
508        }
509    }
510
511    customElements.define("f-cart-items", FCartItems);
512
513}
514
515
516// 注册自定义元素
517
518/* 购物车附加功能 */
519if (!customElements.get("f-cart-addons")) {
520    class FCartAddons extends HTMLElement {
521        constructor() {
522            super();
523            // 定义选择器
524            this.selectors = {
525                zipCode: '[name="address[zip]"]', // 邮编输入框
526                province: '[name="address[province]"]', // 省份选择框
527                country: '[name="address[country]"]', // 国家选择框
528                addressForm: '[data-address="root"]', // 地址表单
529                shippingMessage: ".f-cart-addon__shipping-rate", // 运费信息显示区域
530                cartDiscountCode: '[name="discount"]', // 折扣码输入框
531                cartDiscountCodeNoti: "[data-discount-noti]", // 折扣码通知
532                cartNote: '[name="note"]', // 购物车备注
533                saveAddonButton: ".f-cart-addon--save", // 保存按钮
534                closeAddonButton: ".f-cart-addon--close", // 关闭按钮
535                calcShippingButton: ".f-cart-addon--calculate", // 计算运费按钮
536                triggerAddonButton: ".f-cart-addon--trigger-button", // 触发按钮
537                devliveryTime: '[name="attributes[Delivery time]"]', // 配送时间
538            };
539        }
540
541        // 元素插入DOM时调用
542        connectedCallback() {
543            // 根据页面类型设置购物车容器
544            this.cartWrapper = document.querySelector(".f-cart-drawer");
545            this.isCartPage = MinimogSettings.templateName === "cart";
546            if (this.isCartPage) {
547                this.cartWrapper = document.querySelector(".f-cart__footer--wrapper");
548            }
549
550            this.initAddress = false; // 地址是否初始化标志
551            this.cartOverlay = this.cartWrapper.querySelector(".f-cart__overlay"); // 遮罩层
552            this.domNodes = queryDomNodes(this.selectors, this); // 获取DOM节点
553            this.rootUrl = '/'; // 商店根URL
554            this.discountCodeKey = "minimog-discount-code"; // 折扣码本地存储键
555            this.deliveryCodeKey = "minimog-delivery-code"; // 配送时间本地存储键
556            this.init(); // 初始化
557        }
558
559        // 元素从DOM移除时调用
560        disconnectedCallback() {
561            // 移除所有事件监听
562            if (this._removeCloseAddonButton) this._removeCloseAddonButton();
563            if (this._removeCalcShippingButton) this._removeCalcShippingButton();
564            this.querySelectorAll(this.selectors.triggerAddonButton).forEac
564h((button) => {
565                button.removeEventListener('click', this.handleOpenAddon.bind(this));
566            });
567            this.querySelectorAll(this.selectors.saveAddonButton).forEach((button) => {
568                button.removeEventListener('click', this.handleSaveAddonValue.bind(this));
569            });
570        }
571
572        // 初始化方法
573        init() {
574            const {cartDiscountCode, cartDiscountCodeNoti, devliveryTime} = this.domNodes;
575
576            // 为所有触发按钮添加点击事件
577            this.querySelectorAll(this.selectors.triggerAddonButton).forEach((button) => {
578                button.addEventListener('click', this.handleOpenAddon.bind(this));
579            });
580
581            // 为所有保存按钮添加点击事件
582            this.querySelectorAll(this.selectors.saveAddonButton).forEach((button) => {
583                button.addEventListener('click', this.handleSaveAddonValue.bind(this));
584            });
585
586            // 添加关闭按钮事件委托
587            this._removeCloseAddonButton = addEventDelegate({
588                selector: this.selectors.closeAddonButton,
589                context: this.cartWrapper,
590                handler: this.close.bind(this),
591            });
592
593            // 添加计算运费按钮事件委托
594            this._removeCalcShippingButton = addEventDelegate({
595                selector: this.selectors.calcShippingButton,
596                context: this.cartWrapper,
597                handler: this.calcShipping.bind(this),
598            });
599
600            // 从本地存储恢复折扣码
601            if (cartDiscountCode) {
602                const code = localStorage.getItem(this.discountCodeKey);
603                if (code) {
604                    cartDiscountCode.value = code;
605                    if (cartDiscountCodeNoti) {
606                        cartDiscountCodeNoti.style.display = "inline";
607                    }
608                }
609            }
610
611            // 从本地存储恢复配送时间
612            if (devliveryTime) {
613                const code = localStorage.getItem(this.deliveryCodeKey);
614                if (code) devliveryTime.value = code;
615            }
616
617            // 设置配送时间选择器的最小日期为今天
618            const today = new Date().toISOString().slice(0, 16);
619            const deliveryTimeElm = this.querySelector("#delivery-time");
620            if (deliveryTimeElm) deliveryTimeElm.min = today;
621        }
622
623        // 处理打开附加功能面板
624        handleOpenAddon(e) {
625            e.preventDefault();
626            const {target} = e;
627
628            // 在购物车页面,关闭其他已打开的面板
629            if (this.isCartPage) {
630                const addonCurrentActive = document.querySelector(".f-cart-addon__body.open");
631                if (addonCurrentActive) addonCurrentActive.classList.remove("open");
632            }
633
634            const {open: addonTarget} = target.dataset;
635            const addonNode = this.cartWrapper.querySelector(`#f-addons-${addonTarget}`);
636
637            // 移除所有按钮的激活状态
638            this.removeActiveAllButton();
639
640            // 设置当前按钮和面板为激活状态
641            target.classList.add("active");
642            addonNode && addonNode.classList.add("open");
643            this.cartOverlay && this.cartOverlay.classList.add("open");
644            this.openAddon = addonNode;
645
646            // 如果是运费计算面板,加载国家选项
647            if (addonTarget === "shipping") {
648                fetchSection("country-options", {url: window.MinimogSettings.base_url})
649                    .then((html) => {
650                        const select = html.querySelector("#AddressCountry");
651                        const options = select && select.querySelectorAll("option");
652                        const defaultSelect = addonNode.querySelector("#MadrressCountry select");
653                        options &&
654                        options.forEach((option) => {
655                            defaultSelect && defaultSelect.appendChild(option);
656                        });
657                        this.setupCountries();
658                        defaultSelect.value = defaultSelect && defaultSelect.dataset.default;
659                    })
660                    .catch(console.error);
661            }
662        }
663
664        // 移除所有触发按钮的激活状态
665        removeActiveAllButton() {
666            const triggerButtons = this.querySelectorAll(this.selectors.triggerAddonButton);
667            triggerButtons && triggerButtons.forEach((button) => button.classList.remove("active"));
668        }
669
670        // 初始化国家/省份选择器
671        setupCountries() {
672            if (this.initAddress) return;
673            if (Shopify && Shopify.CountryProvinceSelector) {
674                // 使用Shopify提供的国家/省份选择器
675                new Shopify.CountryProvinceSelector("AddressCountry", "AddressProvince", {
676                    hideElement: "AddressProvinceContainer",
677                });
678                this.initAddress = true;
679            }
680        }
681
682        // 关闭附加功能面板
683        close(event) {
684            event.preventDefault();
685            this.openAddon.classList.remove("open");
686            this.cartOverlay && this.cartOverlay.classList.remove("open");
687            this.removeActiveAllButton();
688            this.openAddon = null;
689        }
690
691        // 计算运费
692        calcShipping(event) {
693            event.preventDefault();
694            const actionsWrapper = event.target.closest(".f-cart-addon__action");
695            actionsWrapper.classList.add("f-spinner-loading"); // 显示加载状态
696
697            // 获取输入值
698            const zipCode = this.domNodes.zipCode && this.domNodes.zipCode.value && this.domNodes.zipCode.value.trim();
699            const country = this.domNodes.country.value;
700            const province = this.domNodes.province.value;
701
702            // 重置运费信息显示
703            this.domNodes.shippingMessage.classList.remove("error");
704            this.domNodes.shippingMessage.innerHTML = "";
705
706            // 是否显示配送天数
707            const showDeliveryDays = actionsWrapper.dataset.showDeliveryDays === "1";
708
709            // 发送运费计算请求
710            fetch(
711                `${this.rootUrl}cart/shipping_rates.json?shipping_address%5Bzip%5D=${zipCode}&shipping_address%5Bcountry%5D=${country}&shipping_address%5Bprovince%5D=${province}`
712            )
713                .then((res) => res.json())
714                .then((res) => {
715                    if (res && res.shipping_rates) {
716                        const {shipping_rates} = res;
717                        const {shippingRatesResult, noShippingRate} = MinimogStrings;
718
719                        if (shipping_rates.length > 0) {
720                            actionsWrapper.classList.remove("f-spinner-loading");
721                            const shippingLabel = document.createElement("P");
722                            shippingLabel.classList.add("f-cart-addon__shipping-rate--label");
723                            shippingLabel.innerHTML = `${shippingRatesResult.replace("{{count}}", shipping_rates.length)}:`;
724                            this.domNodes.shippingMessage.appendChild(shippingLabel);
725
726                            // 显示每个运费选项
727                            shipping_rates.map((rate) => {
728                                const {deliveryOne = "Day", deliveryOther = "Days"} = actionsWrapper.dataset;
729                                let deliveryDays = "";
730
731                                // 如果有配送天数信息
732                                if (rate.delivery_days.length > 0 && showDeliveryDays) {
733                                    let textDay = deliveryOne;
734                                    const firstDeliveryDay = rate.delivery_days[0];
735                                    const lastDeliveryDay = rate.delivery_days.at(-1);
736                                    if (firstDeliveryDay > 1) textDay = deliveryOther;
737
738                                    // 格式化配送天数显示
739                                    if (firstDeliveryDay === lastDeliveryDay) {
740                                        deliveryDays = `(${firstDeliveryDay} ${textDay})`;
741                                    } else {
742                                        deliveryDays = `(${firstDeliveryDay} - ${lastDeliveryDay} ${textDay})`;
743                                    }
744                                }
745
746                                // 创建运费选项元素
747                                const shippingRateItem = document.createElement("P");
748                                shippingRateItem.classList.add("f-cart-addon__shipping-rate--item");
749                                shippingRateItem.innerHTML = `${rate.name}: <span>${rate.price} ${Shopify.currency.active}</span> ${deliveryDays}`;
750                                this.domNodes.shippingMessage.appendChild(shippingRateItem);
751                            });
752                        } else {
753                            // 没有可用运费选项
754                            actionsWrapper.classList.remove("f-spinner-loading");
755                            this.domNodes.shippingMessage.innerHTML = `<p>${noShippingRate}</p>`;
756                        }
757                    } else {
758                        // 显示错误信息
759                        actionsWrapper.classList.remove("f-spinner-loading");
760                        Object.entries(res).map((error) => {
761                            this.domNodes.shippingMessage.classList.add(error[0] && error[0].toLowerCase());
762                            const message = `${error[1][0]}`;
763                            const shippingRateError = document.createElement("P");
764                            shippingRateError.classList.add("f-cart-addon__shipping-rate--error");
765                            shippingRateError.innerHTML = `${message}<sup>*</sup>`;
766                            this.domNodes.shippingMessage.appendChild(shippingRateError);
767                        });
768                    }
769                })
770                .catch(console.error);
771        }
772
773        // 处理保存附加功能值
774        handleSaveAddonValue(e) {
775            e.preventDefault();
776            const {target} = e;
777            const {cartDiscountCode, cartDiscountCodeNoti, devliveryTime} = this.domNodes;
778
779            // 折扣码处理
780            if (target.dataset.action === "coupon" && cartDiscountCode) {
781                const code = cartDiscountCode.value;
782                localStorage.setItem(this.discountCodeKey, code); // 保存到本地存储
783
784                // 显示或隐藏折扣码通知
785                if (code !== "" && cartDiscountCodeNoti) {
786                    cartDiscountCodeNoti.style.display = "inline";
787                } else {
788                    cartDiscountCodeNoti.style.display = "none";
789                }
790                this.close(e);
791            }
792
793            // 备注处理
794            if (target.dataset.action === "note") {
795                this.updateCartNote();
796                this.close(e);
797            }
798
799            // 配送时间处理
800            if (target.dataset.action === "delivery") {
801                const code = devliveryTime.value;
802                const isValidDate = Date.parse(code);
803
804                // 验证日期有效性
805                if (isValidDate > Date.now()) {
806                    localStorage.setItem(this.deliveryCodeKey, code);
807                    this.close(e);
808                } else {
809                    // 无效日期处理
810                    localStorage.setItem(this.deliveryCodeKey, "");
811                    devliveryTime.value = "";
812                    window.MinimogTheme.Notification.show({
813                        target: this.querySelector(".f-cart-addon-message-error"),
814                        method: "appendChild",
815                        type: "error",
816                        message: window.MinimogStrings.valideDateTimeDelivery,
817                        last: 3000,
818                        sticky: false,
819                    });
820                }
821            }
822        }
823
824        // 更新购物车备注
825        updateCartNote() {
826            const cartNoteValue = this.domNodes.cartNote.value;
827            const body = JSON.stringify({note: cartNoteValue});
828            fetch(`${window.MinimogSettings.routes.cart_update_url}`, {...fetchConfig(), ...{body}});
829        }
830    }
831
832    // 注册自定义元素
833    customElements.define("f-cart-addons", FCartAddons);
834}
835if (!customElements.get("fec-cart-checkout-button")) {
836    class FecCartCheckoutButton extends HTMLElement {//侧栏购物车和购物车页的结账按钮
837        connectedCallback() {
838            this.targetUrl = this.dataset.targetUrl;
839            this.productFormId = this.dataset.productFormId;
840            this.time = null
841            this.addEventListener('click', e => {
842                const button = this.querySelector('button');
843                if (button && e.target === button) {
844                    this.checkoutHandler(button);
845                }
846            });
847        }
848
849        checkoutHandler(button) {
850
851            // 1. 显示 loading
852            if (button.disabled === true) {
853                return;
854            }
855            button.disabled = true;
856
857            clearTimeout(this.time);
858            this.time = setTimeout(() => {
859                button.disabled = false;
860            }, 3000)
861
862            $log('点击结账', this.targetUrl);
863            api.post('/api/checkout/order/cart-checkout', {
864                client_detail: fGetClientInfo()
865            }).then(res => {
866                window.location.href = $buildUrl(this.targetUrl , '/' + res.order_number)
867            }).catch((err) => {
868                if (err.code == '101108002') {//库存不足
869                    FecTheme.Notification.show({
870                        target: document.querySelector('.checkout-error-wrapper'),
871                        method: "appendChild",
872                        type: "error",
873                        message: this.dataset.soldOut,
874                        delay: 100,
875                    })
876                } else if (err.code == '101108001') {//状态下架,或者产品被删除
877                    FecTheme.Notification.show({
878                        target: document.querySelector('.checkout-error-wrapper'),
879                        method: "appendChild",
880                        type: "error",
881                        message: this.dataset.notAvailable,
882                        delay: 100,
883                    })
884                } else if (err.code == '101000012' || err.code == '101000013') {
885                    FecTheme.Notification.show({
886                        target: document.querySelector('.checkout-error-wrapper'),
887                        method: "appendChild",
888                        type: "error",
889                        message: err.message || 'The conditions are not met.',
890                        delay: 100,
891                    })
892                } else {
893                    FecTheme.Notification.show({
894                        target: document.querySelector('.checkout-error-wrapper'),
895                        method: "appendChild",
896                        type: "error",
897                        message: err.message || 'The conditions are not met.',
898                        delay: 100,
899                    })
900                }
901            }).finally(() => {
902                button.disabled = false
903            })
904        }
905    }
906
907    customElements.define('fec-cart-checkout-button', FecCartCheckoutButton);
908
909}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.