1class ScriptProductDetail extends Script { 2 constructor(params) { 3 super(params) 4 this._attrs = { 5 lastWindowWidth: window.innerWidth 6 } 7 this._original = {} 8 this.params = params 9 this.hasBuyOn = params.hasBuyOn 10 this.showContent = JSON.parse(this.utilts.htmlDecode(params.showContent)) 11 this.show_quote_list = params.show_quote_list 12 this.isM = window.innerWidth < 768 13 this.$dom = document.querySelector(`#${this.attrId}`) 14 this.$iconGroup = null 15 this.$btnGroup = null 16 this.patternModal = null 17 this.isShop = window.app.info.site.is_shop 18 this.loadingTarget = 96 19 this.loadingTimeout = null 20 } 21 async init() { 22 this.beforeInit() 23 this.checkStatus() 24 await this.initScript() 25 this.afterInit() 26 } 27 28 beforeInit() { 29 const params = this.params 30 this._original.direction = this.params._direction || this.params.direction; // ä¿ååå§çæ¹å 31 const isMobi = window.innerWidth < 1200 32 if (isMobi) { 33 params.direction = 'bottom' 34 } 35 } 36 37 afterInit() { 38 const unit = document.querySelector(`#${this.attrId} .unit-product-detail`); 39 unit && unit.removeAttribute('before-init') 40 const params = this.params 41 const that = this; 42 const allClasses = ['unit-detail-album--x', 'unit-detail-album--y', 'unit-detail-album--top', 'unit-detail-album--bottom', 'unit-detail-album--left', 'unit-detail-album--right'] 43 const directMap = { 44 top: { 45 direction: 'horizontal', 46 classList: ['unit-detail-album--y', 'unit-detail-album--top'] 47 }, 48 bottom: { 49 direction: 'horizontal', 50 classList: ['unit-detail-album--y', 'unit-detail-album--bottom'] 51 }, 52 left: { 53 direction: 'vertical', 54 classList: ['unit-detail-album--x', 'unit-detail-album--left'] 55 }, 56 right: { 57 direction: 'vertical', 58 classList: ['unit-detail-album--x', 'unit-detail-album--right'] 59 } 60 }; 61 const thumb = that.$dom.querySelector('.unit-detail-album__thumb') 62 window.addEventListener('resize', function () { 63 if (Math.abs(that._attrs.lastWindowWidth - window.innerWidth) <= 20) { 64 // é宽度ååä¸å¤ç 65 return; 66 } 67 if (!window.hasOwnProperty('_lf_timers_')) { 68 window._lf_timers_ = {} 69 } 70 that._attrs.lastWindowWidth = window.innerWidth 71 const timer = params.attrId + '@resize'; 72 if (window._lf_timers_[timer]) { 73 clearTimeout(window._lf_timers_[timer]) 74 delete window._lf_timers_[timer] 75 } else { 76 thumb.setAttribute('hidden', 'true') 77 } 78 window._lf_timers_[timer] = setTimeout(function () { 79 delete window._lf_timers_[timer]; 80 const isMobi = window.innerWidth < 1200 81 const direct = that._original.direction 82 let direction = !isMobi && direct && directMap.hasOwnProperty(direct) ? direct : 'bottom' 83 const targetClassList = directMap[direction].classList 84 if (isMobi) { 85 unit.classList.add('unit-product-detail-mobi') 86 } else { 87 unit.classList.remove('unit-product-detail-mobi') 88 } 89 that.$dom.classList.remove.apply(that.$dom.classList, allClasses); 90 that.$dom.classList.add.apply(that.$dom.classList, targetClassList); 91 thumb.removeAttribute('hidden') 92 that.unitAlbum.swiperThumb.changeDirection(directMap[direction].direction).update() 93 that.unitAlbum.swiperPicture.update() 94 }, 500) 95 }) 96 97 window.addEventListener('evt_open_product_inquiry', function () { 98 document.querySelector('.unit-detail-button-inquiry').click() 99 }) 100 101 const promptLogin = this.utilts.throttle(function () { 102 const message = Utilts.ins().$t('Please login in first') 103 if (confirm(message)) window.location.href = globalThis.Server.getRinseHref('/login.html', window.app.info.site) 104 }, 200) 105 106 window.addEventListener('evt_required_login', function () { 107 promptLogin(); 108 }) 109 setTimeout(() => { 110 if (!this.showContent.includes('btn') || !window.app.info.productDetail.config.button_authority.includes('inquiry')){ 111 const allProductInquiryBtns = document.querySelectorAll('.unit-detail-html-tabs__content-html [cloud-editor="unit"][type="cebutton"] [ce-data-type="link"][target="product_inquiry"]') 112 allProductInquiryBtns.forEach(val => { 113 val.classList.add('d-none') 114 }) 115 } 116 }, 100) 117 } 118 119 // ç§»å¨ç«¯æ§è¡ 120 initMobile() { 121 if (document.querySelector('.unit-detail-footer') || window.app.utilts.checkDesign()) return 122 this.initFooter() 123 this.initBtn() 124 this.initPatternModal() 125 } 126 // åå§ååºé¨ 127 initFooter() { 128 const $app = document.getElementById('app') 129 const $div = document.createElement('div') 130 const $div1 = document.createElement('div') 131 const $div2 = document.createElement('div') 132 $app.classList.add('with-footer') 133 $div.classList.add('unit-detail-footer') 134 $div1.classList.add('unit-detail-footer_icon-group') 135 $div2.classList.add('unit-detail-footer_btn-group') 136 $div.appendChild($div1) 137 $div.appendChild($div2) 138 $app.appendChild($div) 139 this.$iconGroup = $div1 140 this.$btnGroup = $div2 141 document.querySelector('.widget-toTop-wrap').style.bottom = 'calc(80px + var(--SAIB))' 142 } 143 // åå§åæé®ç» 144 initBtn() { 145 // 客ææé® 146 if (document.getElementById('widgetSocialMediaChat')) { 147 const $service = document.createElement('div') 148 $service.classList.add('unit-detail-button-service') 149 $service.innerHTML = `<span class="iconfont icon-kefu-line"></span>` 150 this.$iconGroup.append($service) 151 $service.addEventListener('click', () => { 152 document.getElementById('widgetSocialMediaChat').querySelector('.widget-social-media-chat__server').click() 153 }) 154 } 155 156 // 2bæ¯æ²¡æè´ä¹°æé®ç 157 if (this.isShop && window.app.info.productDetail.status) { 158 const $buy = this.$dom.querySelector('.unit-detail-button-buy') 159 const $cart = this.$dom.querySelector('.unit-detail-button-cart') 160 this.$btnGroup.append($cart) 161 this.$btnGroup.append($buy) 162 } 163 const $comment = this.$dom.querySelector('.unit-detail-button-comment') 164 const $inquiry = this.$dom.querySelector('.unit-detail-button-inquiry') 165 this.calcInquiryBtn($inquiry) 166 if ($comment) this.$iconGroup.append($comment) 167 if ($inquiry) this.$iconGroup.append($inquiry) 168 this.calcQuoteBtn() 169 } 170 // åå§å款å¼åæ°å¼¹çª 171 initPatternModal() { 172 if (!this.patternModal) { 173 this.patternModal = new Modal() 174 this.patternModal.init({ 175 title: '', 176 body: this.getPatternGroup(), 177 size: 'lg', 178 className: "pattern-modal", 179 submitText: window.app.utilts.$t('Buy Now'), 180 cancelText: window.app.utilts.$t('Add To Cart'), 181 isCloseNoCancel: true, 182 onSubmit: () => { 183 this.utilts.setBtnLoadingStatus(this.patternModal.$submit, true) 184 this.unitButtonBuy.outerBuy() 185 setTimeout(() => { 186 this.utilts.setBtnLoadingStatus(this.patternModal.$submit, false) 187 }, 1000) 188 }, 189 onCancel: () => { 190 this.utilts.setBtnLoadingStatus(this.patternModal.$cancel, true) 191 this.unitButtonCart.outerAddCart() 192 setTimeout(() => { 193 this.utilts.setBtnLoadingStatus(this.patternModal.$cancel, false) 194 }, 1000) 195 } 196 }) 197 } 198 } 199 // å¼ç¨åç»ä»¶çjs
200 async initScript() { 201 await Promise.all([ 202 new Load('/assets/js/unit/detail-album.js', 'application/javascript', {version_enabled: true}), 203 new Load('/assets/js/unit/detail-price.js', 'application/javascript', {version_enabled: true}), 204 new Load('/assets/js/unit/detail-comment-score.js', 'application/javascript', {version_enabled: true}), 205 new Load('/assets/js/unit/detail-like.js', 'application/javascript', {version_enabled: true}), 206 new Load('/assets/js/unit/detail-order-quantity.js', 'application/javascript', {version_enabled: true}), 207 new Load('/assets/js/unit/detail-pattern.js', 'application/javascript', {version_enabled: true}), 208 new Load('/assets/js/unit/detail-quantity.js', 'application/javascript', {version_enabled: true}), 209 new Load('/assets/js/unit/detail-logistics.js', 'application/javascript', {version_enabled: true}), 210 new Load('/assets/js/unit/detail-customized.js', 'application/javascript', {version_enabled: true}), 211 new Load('/assets/js/unit/detail-alert-size-chart.js', 'application/javascript', {version_enabled: true}), 212 new Load('/assets/js/unit/detail-button-buy.js', 'application/javascript', {version_enabled: true}), 213 new Load('/assets/js/unit/detail-button-cart.js', 'application/javascript', {version_enabled: true}), 214 new Load('/assets/js/unit/detail-button-comment.js', 'application/javascript', {version_enabled: true}), 215 new Load('/assets/js/unit/detail-button-inquiry.js', 'application/javascript', {version_enabled: true}), 216 new Load('/assets/js/unit/matching-product.js', 'application/javascript', {version_enabled: true}), 217 new Load('/assets/js/unit/detail-payment.js', 'application/javascript', {version_enabled: true}), 218 new Load('/assets/js/unit/detail-wholesale.js', 'application/javascript', {version_enabled: true}), 219 new Load('/assets/js/unit/detail-shipment-time.js', 'application/javascript', {version_enabled: true}), 220 new Load('/assets/js/unit/detail-button-download.js', 'application/javascript', {version_enabled: true}), 221 new Load('/assets/js/unit/detail-button-quote.js', 'application/javascript', {version_enabled: true}), 222 ]) 223 this.unitAlbum = new ScriptDetailAlbum({ ...this.params, name: 'ScriptDetailAlbum' }) 224 if (this.showContent.includes('score')) this.unitCommentScore = new ScriptDetailCommentScore({...this.params, name: 'ScriptDetailCommentScore'}) 225 if (this.showContent.includes('like')) this.unitLike = new ScriptDetailLike({...this.params, name: 'ScriptDetailLike'}) 226 if (this.showContent.includes('pattern')) this.unitPattern = new ScriptDetailPattern({...this.params, name: 'ScriptDetailPattern'}) 227 if (this.showContent.includes('customized')) this.unitCustomized = new ScriptDetailCustomized({...this.params, name: 'ScriptDetailCustomized'}) 228 if (this.showContent.includes('size-chart')) this.unitAlertSizeChart = new ScriptDetailAlertSizeChart({...this.params, name: 'ScriptDetailAlertSizeChart'}) 229 if (this.showContent.includes('btn')) this.unitButtonComment = new ScriptDetailButtonComment({...this.params, name: 'ScriptDetailButtonComment'}) 230 if (this.showContent.includes('btn')) this.unitButtonInquiry = new ScriptDetailButtonInquiry({...this.params, name: 'ScriptDetailButtonInquiry'}) 231 if (this.showContent.includes('matching')) this.unitMatch = new ScriptMatchingProduct({...this.params, name: 'ScriptMatchingProduct'}) 232 if (this.showContent.includes('btn')) this.unitButtonDownload = new ScriptDetailButtonDownload({...this.params, name: 'ScriptDetailButtonDownload'}) 233 if (this.showContent.includes('btn') && this.show_quote_list == 1) this.unitButtonQuote = new ScriptDetailButtonQuote({...this.params, name: 'ScriptDetailButtonQuote'}) 234 // åå 235 if (this.isShop) { 236 if (this.showContent.includes('price')) this.unitPrice = new ScriptDetailPrice({...this.params, name: 'ScriptDetailPrice'}) 237 if (this.showContent.includes('order')) this.unitOrderQuantity = new ScriptOrderQuantity({...this.params, name: 'ScriptOrderQuantity'}) 238 if (this.showContent.includes('logistics')) this.unitLogistics = new ScriptDetailLogistics({...this.params, name: 'ScriptDetailLogistics'}) 239 if (this.showContent.includes('btn')) this.unitButtonBuy = new ScriptDetailButtonBuy({...this.params, name: 'ScriptDetailButtonBuy'}) 240 if (this.showContent.includes('btn')) this.unitButtonCart = new ScriptDetailButtonCart({...this.params, name: 'ScriptDetailButtonCart'}) 241 if (this.showContent.includes('payment')) this.unitPayment = new ScriptDetailPayment({...this.params, name: 'ScriptDetailPayment'}) 242 if (this.showContent.includes('shippingTime')) this.unitShipmentTime = new ScriptDetailShipmentTime({...this.params, name: 'ScriptDetailShipmentTime'}) 243 if (this.showContent.includes('number')) this.unitQuantity = new ScriptDetailQuantity({...this.params, name: 'ScriptDetailQuantity'}) 244 this.unitWholesale = new ScriptDetailWholesale({...this.params, name: 'ScriptDetailWholesale'}) 245 } 246 // 类对象æ å° 247 this.unitScript = { 248 ScriptDetailAlbum: this.unitAlbum || null, 249 ScriptDetailCommentScore: this.unitCommentScore || null, 250 ScriptDetailLike: this.unitLike || null, 251 ScriptDetailPattern: this.unitPattern || null, 252 ScriptDetailQuantity: this.unitQuantity || null, 253 ScriptDetailCustomized: this.unitCustomized || null, 254 ScriptDetailAlertSizeChart: this.unitAlertSizeChart || null, 255 ScriptDetailButtonComment: this.unitButtonComment || null, 256 ScriptDetailButtonInquiry: this.unitButtonInquiry || null, 257 ScriptMatchingProduct: this.unitMatch || null, 258 ScriptDetailPrice: this.unitPrice || null, 259 ScriptOrderQuantity: this.unitOrderQuantity || null, 260 ScriptDetailLogistics: this.unitLogistics || null, 261 ScriptDetailButtonBuy: this.unitButtonBuy || null, 262 ScriptDetailButtonCart: this.unitButtonCart || null, 263 ScriptDetailWholesale: this.unitWholesale || null, 264 ScriptDetailPayment: this.unitPayment || null, 265 ScriptDetailShipmentTime: this.unitShipmentTime || null, 266 ScriptDetailButtonDownload: this.unitButtonDownload || null, 267 ScriptDetailButtonQuote: this.unitButtonQuote || null, 268 } 269 // ç±»åå§å 270 for (const key in this.unitScript) { 271 if (Object.hasOwnProperty.call(this.unitScript, key) && this.unitScript[key]) { 272 this.unitScript[key].init() 273 } 274 } 275 } 276 // 渲æ 277 render() { 278 // ç±»åå§å 279 for (const key in this.unitScript) { 280 if (Object.hasOwnProperty.call(this.unitScript, key) && this.unitScript[key] && !window.app.info.noRenderList.includes(key)) { 281 this.unitScript[key].render() 282 } 283 } 284 window.app.info.noRenderList = [] 285 if (this.isM) this.initMobile() 286 this.updatePatterView() 287 } 288 // 款å¼åæ°å¼¹åºæ¡ 289 renderPattern(params) { 290 // mode { 0: 没ææé® 1: è´ç©è½¦åç«å³è´ä¹° 2ï¼ è´ç©è½¦ 3ï¼ ç«å³è´ä¹° } æ¾ç¤ºåªä¸ªæé® 291 let mode = params.mode || 1 292 mode = this.hasBuyOn ? 0 : mode 293 if (!this.isShop) mode = 0 294 switch (mode) { 295 case 0: 296 this.patternModal.$footer.style.display = 'none' 297 break 298 case 2: 299 this.patternModal.$submitWrap.style.display = 'none' 300 break 301 case 3: 302 this.patternModal.$cancelWrap.style.display = 'none' 303 break 304 default: 305 break 306 } 307 this.setBtnDisable() 308 this.updatePatterView() 309 this.patternModal.open() 310 } 311 setBtnStyleDisable(){ 312 this.patternModal.$cancel.setAttribute('disabled', 'disabled') 313 this.patternModal.$cancel.classList.add('btn-disabled') 314 this.patternModal.$submit.setAttribute('disabled', 'disabled') 315 this.patternModal.$submit.classList.add('btn-disabled') 316 this.patternModal.$cancel.classList.add('d-none') 317 this.patternModal.$submit.classList.add('d-none') 318 } 319 cancelBtnDisable(){ 320 this.patternModal.$submit.removeAttribute('disabled') 321 this.patternModal.$cancel.removeAttribute('disabled') 322 this.patternModal.$cancel.classList.remove('btn-disabled') 323 this.patternModal.$submit.classList.remove('btn-disabled') 324 this.patternModal.$cancel.classList.remove('d-none') 325 this.patternModal.$submit.classList.remove('d-none') 326 } 327 setBtnDisable(){ 328 // console.log('movebtn') 329 if(window.app.info.productDetail&&window.app.info.productDetail.pattern&&window.app.info.productDetail.pattern.every(a=>a.selling_price===0)){ 330 this.setBtnStyleDisable() 331 } 332 if(window?.app?.info?.productDetail?.is_pattern){ 333 if (window.app.info.productDetail.min_price <= 0 || !window.app.info.productDetail.is_match_paramter) { 334 this.setBtnStyleDisable() 335 }else{ 336 this.cancelBtnDisable() 337 } 338 if(window?.app?.info?.productDetail?.is_discount){ 339 if (window?.app?.info?.productDetail?.discount_price <= 0) { 340 this.setBtnStyleDisable() 341 }else{ 342 this.cancelBtnDisable() 343 } 344 } 345 } 346 } 347 updatePatterView() { 348 if (!this.patternModal) return 349 const $header = this.patternModal.$target.querySelector('.unit-detail_pattern-group-header') 350 const $img = $header.querySelector('.unit-detail_pattern-group-image img') 351 const $price = $header.querySelector('.unit-detail_pattern-group-price') 352 const $attr = $header.querySelector('.unit-detail-pattern__attr') 353 if (this.$dom.querySelector('.unit-detail-price--prices')) $price.innerHTML = this.$dom.querySelector('.unit-detail-price--prices').innerHTML 354 if (this.$dom.querySelector('.unit-detail-pattern__attr-items')) $attr.innerHTML = this.$dom.querySelector('.unit-detail-pattern__attr-items').innerHTML 355 if (window.app.info.productDetail.thumb) $img.src = window.app.info.productDetail.thumb 356 } 357 // åå§å款å¼åæ°ç» 358 getPatternGroup() { 359 const $div = document.createElement('div') 360 $div.classList.add('unit-detail_pattern-group') 361 $div.innerHTML = ` 362 <div class="unit-detail_pattern-group-header"> 363 <div class="unit-detail_pattern-group-image"><img src="${window.app.info.page.detail_thumb}" class="img-fluid" /></div> 364 <div class="unit-detail_pattern-group-info"> 365 <div class="unit-detail_pattern-group-price"></div> 366 <div class="unit-detail-pattern__attr"></div> 367 </div> 368 </div> 369 <div class="unit-detail_pattern-group-body"></div> 370 ` 371 const $body = $div.querySelector('.unit-detail_pattern-group-body') 372 const $pattern = this.$dom.querySelector('.unit-detail-pattern__items') 373 const $quantity = this.$dom.querySelector('.unit-detail-quantity') 374 if ($pattern) $body.appendChild($pattern) 375 if ($quantity) $body.appendChild($quantity) 376 return $div 377 } 378 // 询çæé®ç¹æ®å¤ç 379 calcInquiryBtn($el) { 380 const is_only_inquiry = window.app.info.productDetail.config.button_authority.length == 1 && window.app.info.productDetail.config.button_authority[0] == 'inquiry' 381 if (this.isShop && !is_only_inquiry && window.app.info.productDetail.min_price > 0) { 382 $el.querySelector('.btn').innerHTML = `<span class="iconfont icon-pinglun"></span>` 383 $el.querySelector('.btn').classList.remove('btn') 384 } else { 385 this.$iconGroup.style.width = "100%" 386 $el?.classList.add('full') 387 } 388 } 389 calcQuoteBtn() { 390 if (!(this.isShop && window.app.info.productDetail.min_price > 0) || window.app.info.productDetail.config.button_authority.length < 2) { 391 const $quote = this.$dom.querySelector('.unit-detail-button-quote') 392 if ($quote) this.$iconGroup.append($quote) 393 } 394 } 395 checkStatus() {
396 let detailPopupNode = document.getElementById('detail-popup') 397 if (this.params.templateStatus === 1 && !detailPopupNode) { 398 detailPopupNode = document.createElement('div') 399 detailPopupNode.innerHTML = ` 400 <div class="unit-detail-popup__window"> 401 <div class="unit-detail-popup__content"> 402 <div><img src="https://img.yfisher.com/design/loading1.gif"></div> 403 <div class="progress"><div class="progress-bar"></div></div> 404 <div>${window.app.utilts.$t('The product details page is being generated. <br>You can view the generated results later.')}</div> 405 <div class="unit-detail-popup__button"> 406 <a class="refresh-button btn" href=""> 407 <div class="d-inline-block"> 408 <span class="iconfont icon-refresh"></span> ${window.app.utilts.$t('Refresh page')} 409 </div> 410 </a> 411 <a class="goback-button btn" href="./"> 412 <div class="d-inline-block">${window.app.utilts.$t('Return to Home')}</div> 413 </a> 414 </div> 415 </div> 416 </div>` 417 418 detailPopupNode.setAttribute('id', 'detail-popup') 419 detailPopupNode.className = "unit-detail-popup__window-wrap show" 420 document.body.appendChild(detailPopupNode) 421 document.body.style.overflow = 'hidden' 422 423 const self = this 424 this.loadingTimeout = setInterval(async function(){ 425 const pageData = await self.getPageData() 426 if (pageData.data.content.template_status > 2) { 427 clearInterval(self.loadingTimeout) 428 self.loadingTarget = 300 429 } 430 }, 10 * 1000) 431 432 setTimeout(() => self.runProgress(), 100) 433 } 434 } 435 runProgress() { 436 const self = this 437 const $bar = document.getElementById('detail-popup').querySelector('.progress-bar') 438 let currentWidth = parseFloat($bar.style.width || 0) 439 currentWidth = currentWidth + (this.loadingTarget - currentWidth) / 150 440 $bar.style.width = (currentWidth > 100 ? 100 : currentWidth) + '%' 441 if (currentWidth >= 100) window.location.reload() 442 else setTimeout(() => self.runProgress(), 100) 443 } 444 /** è·å页颿°æ® */ 445 getPageData() { 446 return new Promise((resolve, reject) => { 447 const params = { 448 url_path: window.app.info.page.page_url, 449 site_id: window.app.info.site.site_id, 450 language_code: window.app.info.site.language_code 451 } 452 this.utilts.request({ 453 url: '/page-url/find-by-path-with-content', 454 method: 'POST', 455 params 456 }).then(res => { 457 resolve(res) 458 }) 459 .catch(error => reject(error)) 460 }) 461 } 462}
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.