| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506 |
- const siteHeader = document.getElementById('siteHeader')
- const mainNavbar = document.getElementById('mainNavbar')
- const navbarToggler = document.querySelector('.navbar-toggler')
- const siteLogoImg = document.getElementById('siteLogoImg')
- const navMode = siteHeader?.dataset?.navMode || 'inner'
- const isHomeNav = navMode === 'home'
- const bootstrapCollapse = mainNavbar && window.bootstrap ? new bootstrap.Collapse(mainNavbar, { toggle: false }) : null
- const setHeroHeaderTheme = (isDark) => {
- if (!siteHeader) return
- siteHeader.classList.remove('hero-theme-light', 'hero-theme-dark')
- siteHeader.classList.add(isDark ? 'hero-theme-dark' : 'hero-theme-light')
- if (siteLogoImg) {
- const lightLogo = siteLogoImg.getAttribute('data-logo-light') || '/assets/xtu/vendor/img/XTU-logo.png'
- const darkLogo = siteLogoImg.getAttribute('data-logo-dark') || '/assets/xtu/vendor/img/XTU-logo2.png'
- siteLogoImg.src = isDark ? darkLogo : lightLogo
- }
- }
- const syncHeroHeaderThemeFromSlide = () => {
- if (!siteHeader || siteHeader.classList.contains('scrolled') || siteHeader.classList.contains('menu-open') || siteHeader.classList.contains('header-search-open')) return
- const activeSlide = document.querySelector('#heroSwiper .swiper-slide-active[data-is-b], #heroSwiper .swiper-slide[data-swiper-slide-index][data-is-b].swiper-slide-active') || document.querySelector('#heroSwiper .swiper-slide[data-is-b]')
- const isB = activeSlide ? String(activeSlide.getAttribute('data-is-b') || '0') : '0'
- setHeroHeaderTheme(isB === '1')
- }
- const syncHeaderState = () => {
- const menuOpen = !!mainNavbar?.classList.contains('show')
- if (siteHeader) siteHeader.classList.toggle('menu-open', menuOpen)
- if (navbarToggler) navbarToggler.setAttribute('aria-expanded', menuOpen ? 'true' : 'false')
- }
- const onScroll = () => {
- if (!siteHeader || !isHomeNav || siteHeader.classList.contains('page-navbar-solid') || siteHeader.classList.contains('header-search-open')) return
- if (window.scrollY > 30) {
- siteHeader.classList.add('scrolled')
- setHeroHeaderTheme(true)
- } else if (!siteHeader.classList.contains('menu-open')) {
- siteHeader.classList.remove('scrolled')
- syncHeroHeaderThemeFromSlide()
- }
- }
- if (isHomeNav) {
- window.addEventListener('scroll', onScroll)
- onScroll()
- } else {
- siteHeader?.classList.add('scrolled')
- setHeroHeaderTheme(true)
- }
- mainNavbar?.addEventListener('show.bs.collapse', () => {
- siteHeader?.classList.add('menu-open', 'scrolled')
- setHeroHeaderTheme(true)
- document.body.classList.add('modal-open')
- syncHeaderState()
- })
- mainNavbar?.addEventListener('hide.bs.collapse', () => {
- siteHeader?.classList.remove('menu-open')
- document.body.classList.remove('modal-open')
- syncHeaderState()
- onScroll()
- })
- mainNavbar?.addEventListener('shown.bs.collapse', syncHeaderState)
- mainNavbar?.addEventListener('hidden.bs.collapse', syncHeaderState)
- mainNavbar?.querySelectorAll('.nav-link:not(.dropdown-toggle), .dropdown-item').forEach((link) => {
- link.addEventListener('click', () => {
- if (window.innerWidth < 992 && mainNavbar.classList.contains('show')) bootstrapCollapse?.hide()
- })
- })
- if (mainNavbar && window.bootstrap) {
- mainNavbar.querySelectorAll('.nav-item.dropdown').forEach((item) => {
- const toggle = item.querySelector(':scope > .dropdown-toggle')
- const menu = item.querySelector(':scope > .dropdown-menu')
- if (!toggle || !menu) return
- const dropdownInstance = bootstrap.Dropdown.getOrCreateInstance(toggle, { autoClose: true })
- let hideTimer = null
- const clearHideTimer = () => {
- if (hideTimer) {
- clearTimeout(hideTimer)
- hideTimer = null
- }
- }
- const showDesktopDropdown = () => {
- if (window.innerWidth < 992 || siteHeader?.classList.contains('header-search-open')) return
- clearHideTimer()
- item.classList.add('show')
- toggle.classList.add('show')
- toggle.setAttribute('aria-expanded', 'true')
- menu.classList.add('show')
- dropdownInstance.show()
- }
- const hideDesktopDropdown = () => {
- if (window.innerWidth < 992) return
- clearHideTimer()
- item.classList.remove('show')
- toggle.classList.remove('show')
- toggle.setAttribute('aria-expanded', 'false')
- menu.classList.remove('show')
- dropdownInstance.hide()
- }
- const scheduleHideDesktopDropdown = () => {
- if (window.innerWidth < 992) return
- clearHideTimer()
- hideTimer = setTimeout(() => hideDesktopDropdown(), 120)
- }
- toggle.removeAttribute('data-bs-toggle')
- item.addEventListener('mouseenter', showDesktopDropdown)
- item.addEventListener('mouseleave', scheduleHideDesktopDropdown)
- menu.addEventListener('mouseenter', showDesktopDropdown)
- menu.addEventListener('mouseleave', scheduleHideDesktopDropdown)
- toggle.addEventListener('focus', showDesktopDropdown)
- item.addEventListener('focusout', () => {
- setTimeout(() => {
- if (!item.contains(document.activeElement)) scheduleHideDesktopDropdown()
- }, 0)
- })
- toggle.addEventListener('click', (e) => {
- if (window.innerWidth >= 992) {
- const href = toggle.getAttribute('href') || ''
- if (!href || href === '#') e.preventDefault()
- }
- })
- window.addEventListener('resize', () => {
- if (window.innerWidth < 992) {
- clearHideTimer()
- hideDesktopDropdown()
- toggle.setAttribute('data-bs-toggle', 'dropdown')
- } else {
- toggle.removeAttribute('data-bs-toggle')
- }
- })
- })
- }
- const openModal = (id) => {
- const modal = document.getElementById(id)
- if (!modal) return
- modal.classList.add('is-open')
- document.body.classList.add('modal-open')
- }
- const closeModal = (id) => {
- const modal = document.getElementById(id)
- if (!modal) return
- modal.classList.remove('is-open')
- if (!document.querySelector('.custom-modal.is-open') && !mainNavbar?.classList.contains('show')) document.body.classList.remove('modal-open')
- }
- document.getElementById('openLanguageModal')?.addEventListener('click', () => openModal('languageModal'))
- document.getElementById('openLanguageModalMobile')?.addEventListener('click', () => openModal('languageModal'))
- document.getElementById('openLanguageModalFooter')?.addEventListener('click', () => openModal('languageModal'))
- document.querySelectorAll('[data-close-modal]').forEach((btn) => btn.addEventListener('click', () => closeModal(btn.dataset.closeModal)))
- document.querySelectorAll('.custom-modal').forEach((modal) => {
- modal.addEventListener('click', (e) => {
- if (e.target === modal) closeModal(modal.id)
- })
- })
- const headerInlineSearch = document.getElementById('headerInlineSearch')
- const openHeaderSearchBtn = document.getElementById('openHeaderSearch')
- const closeHeaderSearchBtn = document.getElementById('closeHeaderSearch')
- const headerInlineSearchInput = document.getElementById('headerInlineSearchInput')
- const headerInlineSearchForm = document.getElementById('headerInlineSearchForm')
- const openHeaderSearch = () => {
- if (!headerInlineSearch || window.innerWidth < 992) return
- siteHeader?.classList.add('header-search-open', 'scrolled')
- setHeroHeaderTheme(true)
- headerInlineSearch.classList.add('is-open')
- headerInlineSearch.setAttribute('aria-hidden', 'false')
- document.body.classList.add('modal-open')
- setTimeout(() => {
- headerInlineSearchInput?.focus()
- if (headerInlineSearchInput?.value) {
- headerInlineSearchInput.setSelectionRange(0, headerInlineSearchInput.value.length)
- }
- }, 60)
- }
- const closeHeaderSearch = () => {
- if (!headerInlineSearch) return
- siteHeader?.classList.remove('header-search-open')
- headerInlineSearch.classList.remove('is-open')
- headerInlineSearch.setAttribute('aria-hidden', 'true')
- headerInlineSearchInput?.blur()
- if (!mainNavbar?.classList.contains('show')) document.body.classList.remove('modal-open')
- if (isHomeNav) {
- onScroll()
- if (window.scrollY <= 30 && !mainNavbar?.classList.contains('show')) syncHeroHeaderThemeFromSlide()
- }
- }
- openHeaderSearchBtn?.addEventListener('click', (e) => {
- e.preventDefault()
- e.stopPropagation()
- openHeaderSearch()
- })
- closeHeaderSearchBtn?.addEventListener('click', (e) => {
- e.preventDefault()
- e.stopPropagation()
- closeHeaderSearch()
- })
- document.getElementById('openSearchModalMobile')?.addEventListener('click', () => {
- window.location.href = '/search'
- })
- headerInlineSearchForm?.addEventListener('submit', (e) => {
- const keyword = (headerInlineSearchInput?.value || '').trim()
- if (!keyword) {
- e.preventDefault()
- headerInlineSearchInput?.focus()
- headerInlineSearchInput?.setCustomValidity(document.documentElement.lang === 'en' ? 'Please enter a keyword' : '请输入搜索关键词')
- headerInlineSearchInput?.reportValidity()
- return
- }
- headerInlineSearchInput?.setCustomValidity('')
- })
- headerInlineSearchInput?.addEventListener('input', (e) => {
- e.target.setCustomValidity('')
- })
- document.addEventListener('keydown', (e) => {
- if (e.key === 'Escape' && headerInlineSearch?.classList.contains('is-open')) closeHeaderSearch()
- })
- document.querySelectorAll('.footer-social-item').forEach((item) => {
- const closeAll = () => document.querySelectorAll('.footer-social-item.is-open').forEach((el) => el.classList.remove('is-open'))
- item.addEventListener('mouseenter', () => item.classList.add('is-open'))
- item.addEventListener('mouseleave', () => item.classList.remove('is-open'))
- item.addEventListener('click', (e) => {
- if (window.matchMedia('(hover: none)').matches) {
- e.preventDefault()
- const willOpen = !item.classList.contains('is-open')
- closeAll()
- if (willOpen) item.classList.add('is-open')
- }
- })
- item.addEventListener('focusin', () => item.classList.add('is-open'))
- item.addEventListener('focusout', () => item.classList.remove('is-open'))
- document.addEventListener('click', (e) => {
- if (!item.contains(e.target)) item.classList.remove('is-open')
- })
- })
- const resetSlideVideos = (selector) => {
- document.querySelectorAll(selector).forEach((video) => {
- try { video.pause(); video.currentTime = 0 } catch {}
- video.classList.remove('is-ready')
- })
- }
- const playSlideVideo = (slide) => {
- if (!slide) return
- const video = slide.querySelector('.slide-video')
- if (!video) return
- const showVideo = () => video.classList.add('is-ready')
- if (video.readyState >= 2) {
- showVideo()
- } else {
- video.addEventListener('loadeddata', showVideo, { once: true })
- video.addEventListener('canplay', showVideo, { once: true })
- }
- video.play().then(showVideo).catch(() => {})
- }
- document.querySelectorAll('.slide-media-layer, .card-media-layer').forEach((wrap) => {
- wrap.addEventListener('click', () => {
- playSlideVideo(wrap.closest('.swiper-slide') || wrap)
- })
- })
- if (document.querySelector('#heroSwiper') && window.Swiper) {
- const heroSlides = Array.from(document.querySelectorAll('#heroSwiper .swiper-slide[data-bullet-title]'))
- const heroBulletTitles = heroSlides.map((slide) => slide.getAttribute('data-bullet-title') || '')
- new Swiper('#heroSwiper', {
- loop: true,
- speed: 900,
- effect: 'fade',
- fadeEffect: { crossFade: true },
- autoplay: { delay: 5000, disableOnInteraction: false },
- navigation: { nextEl: '.hero-next', prevEl: '.hero-prev' },
- pagination: {
- el: '.hero-pagination',
- clickable: true,
- renderBullet: function (index, className) {
- return `<span class="${className} hero-bar-bullet"><em>${heroBulletTitles[index] || ''}</em></span>`
- },
- },
- on: {
- init(swiper) {
- syncHeroHeaderThemeFromSlide()
- playSlideVideo(swiper.slides[swiper.activeIndex])
- },
- slideChangeTransitionStart: () => {
- resetSlideVideos('#heroSwiper .slide-video')
- syncHeroHeaderThemeFromSlide()
- },
- slideChangeTransitionEnd(swiper) {
- playSlideVideo(swiper.slides[swiper.activeIndex])
- }
- },
- })
- syncHeroHeaderThemeFromSlide()
- }
- if (document.querySelector('#showcaseSwiper') && window.Swiper) {
- const showcaseEl = document.querySelector('#showcaseSwiper')
- const showcaseWrapper = showcaseEl ? showcaseEl.querySelector('.swiper-wrapper') : null
- if (showcaseWrapper) {
- const originalSlides = Array.from(showcaseWrapper.querySelectorAll('.swiper-slide'))
- if (originalSlides.length > 0 && originalSlides.length < 4) {
- let i = 0
- while (showcaseWrapper.querySelectorAll('.swiper-slide').length < 4) {
- const clone = originalSlides[i % originalSlides.length].cloneNode(true)
- clone.classList.add('showcase-card-fill-js')
- showcaseWrapper.appendChild(clone)
- i++
- }
- }
- }
- new Swiper('#showcaseSwiper', {
- loop: false,
- rewind: true,
- speed: 800,
- centeredSlides: false,
- watchOverflow: false,
- slidesPerView: 1.08,
- spaceBetween: 12,
- autoplay: {
- delay: 4500,
- disableOnInteraction: false,
- pauseOnMouseEnter: true
- },
- navigation: {
- nextEl: '.showcase-next',
- prevEl: '.showcase-prev'
- },
- pagination: {
- el: '.showcase-pagination',
- clickable: true
- },
- breakpoints: {
- 768: {
- slidesPerView: 1.3,
- spaceBetween: 12
- },
- 992: {
- slidesPerView: 2,
- spaceBetween: 12
- }
- },
- on: {
- init(swiper) {
- const bullets = swiper.pagination && swiper.pagination.bullets ? swiper.pagination.bullets : []
- bullets.forEach((bullet, index) => {
- bullet.style.setProperty('--showcase-progress-delay', index === swiper.activeIndex ? '0ms' : '0ms')
- })
- },
- slideChangeTransitionStart(swiper) {
- resetSlideVideos('#showcaseSwiper .slide-video')
- const bullets = swiper.pagination && swiper.pagination.bullets ? swiper.pagination.bullets : []
- bullets.forEach((bullet) => {
- bullet.style.animation = 'none'
- void bullet.offsetWidth
- bullet.style.animation = ''
- })
- }
- }
- })
- }
- const backToTopBtn = document.getElementById('backToTopBtn')
- const backToTopShowOffset = 320
- const feedbackFloatForm = document.getElementById('feedbackFloatForm')
- const feedbackFloatRatingInput = document.getElementById('feedbackFloatRating')
- const feedbackFloatSubmit = document.getElementById('feedbackFloatSubmit')
- const feedbackFloatOptions = document.querySelectorAll('.feedback-float-option')
- const feedbackFloatPrompt = document.querySelector('.feedback-float-rating-block h3')
- const feedbackFloatMessage = document.getElementById('feedbackFloatMessage')
- const feedbackSourcePageInput = feedbackFloatForm?.querySelector('input[name="source_page"]')
- const feedbackStars = Array.from(document.querySelectorAll('.feedback-star'))
- const feedbackPromptMap = {
- website: '请对“官网体验”进行评价。',
- service: '请对“服务体验”进行评价。',
- product: '请对“产品体验”进行评价。'
- }
- const syncFeedbackPrompt = (value) => {
- const key = value || 'website'
- if (feedbackFloatPrompt && feedbackPromptMap[key]) feedbackFloatPrompt.textContent = feedbackPromptMap[key]
- }
- const setFeedbackMessage = (text, type = '') => {
- if (!feedbackFloatMessage) return
- feedbackFloatMessage.textContent = text || ''
- feedbackFloatMessage.className = 'feedback-float-message' + (type ? ` is-${type}` : '')
- }
- const syncBackToTopVisibility = () => {
- if (!backToTopBtn) return
- const isVisible = window.scrollY > backToTopShowOffset
- backToTopBtn.classList.toggle('is-visible', isVisible)
- }
- backToTopBtn?.addEventListener('click', () => {
- window.scrollTo({ top: 0, behavior: 'smooth' })
- })
- window.addEventListener('scroll', syncBackToTopVisibility, { passive: true })
- syncBackToTopVisibility()
- const setFeedbackRating = (value) => {
- const rating = Number(value) || 0
- if (feedbackFloatRatingInput) feedbackFloatRatingInput.value = String(rating)
- feedbackStars.forEach((star) => {
- const starValue = Number(star.dataset.value || 0)
- star.classList.toggle('is-active', starValue <= rating)
- })
- if (feedbackFloatSubmit) feedbackFloatSubmit.disabled = rating < 1
- }
- feedbackStars.forEach((star) => {
- star.addEventListener('click', () => setFeedbackRating(star.dataset.value || 0))
- })
- feedbackFloatOptions.forEach((item) => {
- const input = item.querySelector('input[type="radio"]')
- input?.addEventListener('change', () => {
- feedbackFloatOptions.forEach((node) => node.classList.remove('is-active'))
- item.classList.add('is-active')
- const value = input.value || 'website'
- syncFeedbackPrompt(value)
- setFeedbackRating(0)
- setFeedbackMessage('')
- })
- })
- if (feedbackSourcePageInput) feedbackSourcePageInput.value = window.location.href
- syncFeedbackPrompt(document.querySelector('.feedback-float-option input[type="radio"]:checked')?.value || 'website')
- feedbackFloatForm?.addEventListener('submit', async (e) => {
- e.preventDefault()
- const rating = Number(feedbackFloatRatingInput?.value || 0) || 0
- if (rating < 1) return
- const submitUrl = feedbackFloatForm.dataset.submitUrl || ''
- if (!submitUrl) {
- setFeedbackMessage('提交地址缺失', 'error')
- return
- }
- const originalText = feedbackFloatSubmit?.textContent || '提交'
- if (feedbackFloatSubmit) {
- feedbackFloatSubmit.disabled = true
- feedbackFloatSubmit.textContent = '提交中...'
- }
- setFeedbackMessage('')
- try {
- const body = new URLSearchParams(new FormData(feedbackFloatForm))
- const res = await fetch(submitUrl, {
- method: 'POST',
- headers: {
- 'X-Requested-With': 'XMLHttpRequest',
- 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
- },
- body: body.toString()
- })
- const data = await res.json()
- if (data.code === 1) {
- setFeedbackMessage(data.msg || '提交成功,感谢你的评价', 'success')
- feedbackFloatForm.reset()
- feedbackFloatOptions.forEach((node, index) => node.classList.toggle('is-active', index === 0))
- syncFeedbackPrompt('website')
- setFeedbackRating(0)
- } else {
- setFeedbackMessage(data.msg || '提交失败,请稍后再试', 'error')
- if (feedbackFloatSubmit) feedbackFloatSubmit.disabled = rating < 1
- }
- } catch (err) {
- setFeedbackMessage('提交失败,请稍后再试', 'error')
- if (feedbackFloatSubmit) feedbackFloatSubmit.disabled = rating < 1
- } finally {
- if (feedbackFloatSubmit) feedbackFloatSubmit.textContent = originalText
- }
- })
|