| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489 |
- {include file="header" /}
- {php}
- $cooperationContactPhone = '';
- $cooperationContactTel = '';
- foreach (['cooperation_phone', 'contact_phone', 'service_phone', 'phone', 'tel'] as $phoneConfigKey) {
- $phoneConfigValue = trim((string)config('site.' . $phoneConfigKey));
- if ($phoneConfigValue === '') {
- continue;
- }
- $phoneDigits = '';
- $phoneAllowed = true;
- $phoneTel = '';
- foreach (preg_split('//u', $phoneConfigValue, -1, PREG_SPLIT_NO_EMPTY) as $phoneChar) {
- if (ctype_digit($phoneChar)) {
- $phoneDigits .= $phoneChar;
- $phoneTel .= $phoneChar;
- continue;
- }
- if ($phoneChar === '+') {
- $phoneTel .= $phoneChar;
- continue;
- }
- if (!in_array($phoneChar, ['(', ')', '-', ' '], true)) {
- $phoneAllowed = false;
- break;
- }
- }
- if ($phoneAllowed && strlen($phoneDigits) >= 6 && mb_strlen($phoneConfigValue, 'UTF-8') <= 40) {
- $cooperationContactPhone = $phoneConfigValue;
- $cooperationContactTel = $phoneTel;
- break;
- }
- }
- {/php}
- <link rel="stylesheet" href="/assets/xtu/cooperation-topbar.css?v=20260508-5">
- <link rel="stylesheet" href="/assets/xtu/cooperation.css?v=20260508-15">
- <main class="cooperation-page" id="cooperationPage" style="--cooperation-bg:url('{$category.image|default='/assets/xtu/vendor/img/appbanner.jpg'}')">
- <div class="cooperation-topbar">
- <div class="cooperation-topbar-inner">
- <div class="cooperation-topbar-title">{$title|default=cms_t('cooperation_contact_title','联系我们')}</div>
- </div>
- </div>
- <div class="cooperation-shell">
- <section class="cooperation-copy">
- <div class="cooperation-bar-inner">
- <h1>{$title|default=cms_t('cooperation_contact_title','联系我们')}</h1>
- <p>{if getCategory($catid,'description')}{:getCategory($catid,'description')}{elseif !empty($category.description) /}{$category.description}{else /}{:cms_t('cooperation_intro','请填写以下信息,我们的商务团队将会尽快与您联系,结合您的行业场景提供产品、方案与合作支持。')}{/if}</p>
- </div>
- </section>
- <section class="cooperation-card" aria-labelledby="cooperationFormTitle">
- <div class="cooperation-card-head">
- <h2 id="cooperationFormTitle">{:cms_t('cooperation_contact_title','联系我们')}</h2>
- <p>{if getCategory($catid,'description')}{:getCategory($catid,'description')}{elseif !empty($category.description) /}{$category.description}{else /}{:cms_t('cooperation_intro','请填写以下信息,我们的商务团队将会尽快与您联系,结合您的行业场景提供产品、方案与合作支持。')}{/if}</p>
- </div>
- <form id="cooperationLeadForm" class="cooperation-form" data-options-url="{:url('index/cooperation/options')}" data-submit-url="{:url('index/cooperation/submit')}">
- <input type="hidden" name="lang" id="cooperationLang" value="{$site_lang|default='cn'}">
- <input type="hidden" name="source_page" id="cooperationSourcePage" value="">
- <input type="hidden" name="form_token" id="cooperationFormToken" value="{:md5(session_id().'cooperation_lead')}">
- <div class="cooperation-honeypot" aria-hidden="true"><input type="text" name="website" tabindex="-1" autocomplete="off" value=""></div>
- <div class="cooperation-form-grid">
- <div class="cooperation-field"><input type="text" name="name" placeholder="{:cms_t('cooperation_name_placeholder','姓名 *')}" maxlength="80" minlength="2" autocomplete="name" required></div>
- <div class="cooperation-field"><input type="tel" name="phone" placeholder="{:cms_t('cooperation_phone_placeholder','电话 *')}" maxlength="40" minlength="6" autocomplete="tel" inputmode="tel" pattern="[0-9+()\-\s]{6,40}" required></div>
- <div class="cooperation-field"><input type="email" name="email" placeholder="{:cms_t('cooperation_email_placeholder','电子邮箱 *')}" maxlength="120" autocomplete="email" required></div>
- <div class="cooperation-field"><input type="text" name="company" placeholder="{:cms_t('cooperation_company_placeholder','公司名称 *')}" maxlength="120" minlength="2" autocomplete="organization" required></div>
- <div class="cooperation-field">
- <select id="cooperationRegion" aria-label="{:cms_t('cooperation_region_label','洲/区域')}">
- <option value="">{:cms_t('cooperation_region_placeholder','洲/区域 *')}</option>
- </select>
- </div>
- <div class="cooperation-field">
- <select name="country" id="cooperationCountry" aria-label="{:cms_t('cooperation_country_label','国家/地区')}" required disabled>
- <option value="">{:cms_t('cooperation_country_placeholder','国家/地区 *')}</option>
- </select>
- </div>
- <div class="cooperation-field is-full">
- <select name="industry" id="cooperationIndustry" aria-label="{:cms_t('cooperation_industry_label','行业')}" required>
- <option value="">{:cms_t('cooperation_industry_placeholder','行业 *')}</option>
- </select>
- </div>
- <div class="cooperation-field is-full">
- <textarea name="content" id="cooperationContent" placeholder="{:cms_t('cooperation_content_placeholder','咨询内容 *')}" maxlength="300" required></textarea>
- <span class="cooperation-count" id="cooperationContentCount">0/300</span>
- </div>
- <div class="cooperation-field is-full" id="cooperationCaptchaWrap" style="display:none;">
- <input type="hidden" id="cooperationCaptchaNeed" value="{$need_captcha|default=0}">
- <label class="screen-reader-text" for="cooperationCaptchaInput">{:cms_t('cooperation_captcha_label','验证码 *')}</label>
- <div class="cooperation-captcha-row">
- <input type="text" name="verify" id="cooperationCaptchaInput" placeholder="{:cms_t('cooperation_captcha_placeholder','请输入验证码')}" maxlength="6" autocomplete="off">
- <a href="javascript:;" class="cooperation-captcha-img-wrap" id="cooperationCaptchaImageLink" aria-label="{:cms_t('cooperation_refresh_captcha','刷新验证码')}"><img src="" alt="{:cms_t('cooperation_captcha_label','验证码')}" id="cooperationCaptchaImage"></a>
- </div>
- </div>
- <div class="cooperation-checks">
- <label><input type="checkbox" name="privacy_agree" value="1" required><span>{:cms_t('cooperation_privacy_prefix','已阅读并同意')}<a href="{:getCategory(44,'url')}" target="_blank" rel="noopener">{:cms_t('cooperation_privacy_link','《隐私政策》')}</a></span></label>
- </div>
- <button class="cooperation-submit" id="cooperationSubmitBtn" type="submit">{:cms_t('cooperation_submit','提交信息')}</button>
- <div class="cooperation-message" id="cooperationMessage" role="status" aria-live="polite"></div>
- <div class="cooperation-contact">
- {if condition="$cooperationContactPhone neq ''"}
- <a href="tel:{$cooperationContactTel}" class="cooperation-contact-link"><i class="bi bi-telephone"></i> {$cooperationContactPhone}</a>
- {/if}
- </div>
- </form>
- </section>
- </div>
- </main>
- {if condition="!empty($content)"}
- <section class="cooperation-content">
- <div class="cooperation-content-inner">{$content|raw}</div>
- </section>
- {/if}
- <script>
- document.body.classList.add('cooperation-page-body');
- (function () {
- const siteHeader = document.getElementById('siteHeader');
- const syncTopbarPosition = function () {
- if (!siteHeader) return;
- const headerRect = siteHeader.getBoundingClientRect();
- document.documentElement.style.setProperty('--xtu-header-height', Math.max(0, Math.ceil(headerRect.bottom)) + 'px');
- };
- syncTopbarPosition();
- window.addEventListener('load', syncTopbarPosition);
- window.addEventListener('resize', syncTopbarPosition);
- window.addEventListener('scroll', syncTopbarPosition, { passive: true });
- if (window.ResizeObserver && siteHeader) {
- new ResizeObserver(syncTopbarPosition).observe(siteHeader);
- }
- })();
- (function () {
- const form = document.getElementById('cooperationLeadForm');
- if (!form) return;
- const regionSelect = document.getElementById('cooperationRegion');
- const countrySelect = document.getElementById('cooperationCountry');
- const industrySelect = document.getElementById('cooperationIndustry');
- const langInput = document.getElementById('cooperationLang');
- const sourcePageInput = document.getElementById('cooperationSourcePage');
- const submitBtn = document.getElementById('cooperationSubmitBtn');
- const messageBox = document.getElementById('cooperationMessage');
- const contentInput = document.getElementById('cooperationContent');
- const contentCount = document.getElementById('cooperationContentCount');
- const optionsUrl = form.getAttribute('data-options-url') || '/index/cooperation/options';
- const submitUrl = form.getAttribute('data-submit-url') || '/index/cooperation/submit';
- let countryTree = [];
- const text = {
- region: {:json_encode(cms_t('cooperation_region_placeholder','洲/区域 *'), JSON_UNESCAPED_UNICODE)},
- country: {:json_encode(cms_t('cooperation_country_placeholder','国家/地区 *'), JSON_UNESCAPED_UNICODE)},
- industry: {:json_encode(cms_t('cooperation_industry_placeholder','行业 *'), JSON_UNESCAPED_UNICODE)},
- loading: {:json_encode(cms_t('cooperation_loading','选项加载中...'), JSON_UNESCAPED_UNICODE)},
- required: {:json_encode(cms_t('cooperation_required','请完整填写必填信息,并同意隐私政策'), JSON_UNESCAPED_UNICODE)},
- invalidEmail: {:json_encode(cms_t('cooperation_invalid_email','请输入正确的电子邮箱'), JSON_UNESCAPED_UNICODE)},
- invalidPhone: {:json_encode(cms_t('cooperation_invalid_phone','请输入正确的电话号码,可包含数字、+、-、空格和括号'), JSON_UNESCAPED_UNICODE)},
- invalidEmailHint: {:json_encode(cms_t('cooperation_invalid_email_hint','请输入正确的电子邮箱格式,如 name@example.com'), JSON_UNESCAPED_UNICODE)},
- tooLong: {:json_encode(cms_t('cooperation_content_too_long','咨询内容请控制在 300 字以内'), JSON_UNESCAPED_UNICODE)},
- fieldTooLong: {:json_encode(cms_t('cooperation_field_too_long','输入内容超过长度限制'), JSON_UNESCAPED_UNICODE)},
- captchaHint: {:json_encode(cms_t('cooperation_captcha_hint','当前提交较频繁,请先完成验证码'), JSON_UNESCAPED_UNICODE)},
- captchaLabel: {:json_encode(cms_t('cooperation_captcha_label','验证码 *'), JSON_UNESCAPED_UNICODE)},
- captchaPlaceholder: {:json_encode(cms_t('cooperation_captcha_placeholder','请输入验证码'), JSON_UNESCAPED_UNICODE)},
- refreshCaptcha: {:json_encode(cms_t('cooperation_refresh_captcha','刷新验证码'), JSON_UNESCAPED_UNICODE)},
- captchaError: {:json_encode(cms_t('cooperation_captcha_error','验证码错误,请重新输入'), JSON_UNESCAPED_UNICODE)},
- sending: {:json_encode(cms_t('cooperation_sending','正在提交...'), JSON_UNESCAPED_UNICODE)},
- success: {:json_encode(cms_t('cooperation_success','提交成功,我们将尽快与您联系'), JSON_UNESCAPED_UNICODE)},
- failed: {:json_encode(cms_t('cooperation_failed','提交失败,请稍后再试'), JSON_UNESCAPED_UNICODE)},
- optionsFailed: {:json_encode(cms_t('cooperation_options_failed','选项加载失败,请刷新页面重试'), JSON_UNESCAPED_UNICODE)}
- };
- function normalizeLang(value) {
- const lang = String(value || 'cn').toLowerCase();
- if (lang === 'zh-cn' || lang === 'zh' || lang === 'cn') return 'cn';
- if (lang.indexOf('en') === 0) return 'en';
- return lang;
- }
- function setMessage(msg, type) {
- if (!messageBox) return;
- messageBox.textContent = msg || '';
- messageBox.className = 'cooperation-message' + (type ? ' is-' + type : '');
- }
- function showSuccessModal(message) {
- let modal = document.getElementById('cooperationSuccessModal');
- if (!modal) {
- modal = document.createElement('div');
- modal.id = 'cooperationSuccessModal';
- modal.className = 'custom-modal custom-modal-cooperation-success';
- modal.setAttribute('aria-hidden', 'true');
- modal.innerHTML = '<div class="custom-modal-dialog cooperation-success-modal-dialog"><div class="custom-modal-box cooperation-success-modal-box"><button class="custom-modal-close" type="button" data-close-modal="cooperationSuccessModal" aria-label="关闭">×</button><div class="cooperation-success-icon"><i class="bi bi-check2-circle"></i></div><h3 class="cooperation-success-title">提交成功</h3><p class="cooperation-success-text"></p><div class="cooperation-success-actions"><button type="button" class="cooperation-success-btn" data-close-modal="cooperationSuccessModal">知道了</button></div></div></div>';
- document.body.appendChild(modal);
- modal.addEventListener('click', function (e) {
- if (e.target === modal) closeSuccessModal();
- });
- }
- const textNode = modal.querySelector('.cooperation-success-text');
- if (textNode) textNode.textContent = message || text.success;
- modal.classList.add('is-open');
- modal.setAttribute('aria-hidden', 'false');
- document.body.classList.add('modal-open');
- modal.querySelector('.cooperation-success-btn')?.focus();
- }
- function closeSuccessModal() {
- const modal = document.getElementById('cooperationSuccessModal');
- if (!modal) return;
- modal.classList.remove('is-open');
- modal.setAttribute('aria-hidden', 'true');
- if (!document.querySelector('.custom-modal.is-open') && !document.querySelector('.header-inline-search.is-open')) {
- document.body.classList.remove('modal-open');
- }
- }
- document.addEventListener('click', function (e) {
- const closeBtn = e.target.closest && e.target.closest('[data-close-modal="cooperationSuccessModal"]');
- if (closeBtn) {
- e.preventDefault();
- closeSuccessModal();
- }
- });
- document.addEventListener('keydown', function (e) {
- if (e.key === 'Escape') closeSuccessModal();
- });
- function safeText(value) {
- return String(value == null ? '' : value);
- }
- function syncSelectState(select) {
- if (!select) return;
- select.classList.toggle('is-placeholder', !select.value);
- }
- function showCaptcha(force) {
- const wrap = document.getElementById('cooperationCaptchaWrap');
- const input = document.getElementById('cooperationCaptchaInput');
- const image = document.getElementById('cooperationCaptchaImage');
- const link = document.getElementById('cooperationCaptchaImageLink');
- if (!wrap || !input || !image) return;
- wrap.style.display = force ? '' : 'none';
- input.required = !!force;
- if (force && image.src.indexOf('/captcha') === -1) {
- image.src = '/index/cooperation/captcha?t=' + Date.now();
- }
- if (link) {
- link.onclick = function () {
- image.src = '/index/cooperation/captcha?t=' + Date.now();
- return false;
- };
- }
- }
- function resetSelect(select, placeholder) {
- if (!select) return;
- select.innerHTML = '';
- const option = document.createElement('option');
- option.value = '';
- option.textContent = placeholder;
- option.disabled = true;
- option.selected = true;
- select.appendChild(option);
- syncSelectState(select);
- }
- function appendOption(select, value, label) {
- if (!select) return;
- const option = document.createElement('option');
- option.value = safeText(value || label);
- option.textContent = safeText(label || value);
- select.appendChild(option);
- }
- function fillCountries(regionValue) {
- resetSelect(countrySelect, text.country);
- const region = countryTree.find(function (item) {
- return String(item.value || item.title || '') === String(regionValue || '');
- });
- const countries = region && Array.isArray(region.children) ? region.children : [];
- countries.forEach(function (item) {
- appendOption(countrySelect, item.value, item.title || item.name || item.value);
- });
- if (countrySelect) {
- countrySelect.disabled = !countries.length;
- syncSelectState(countrySelect);
- }
- }
- async function loadOptions() {
- const lang = normalizeLang(langInput ? langInput.value : 'cn');
- if (langInput) langInput.value = lang;
- resetSelect(regionSelect, text.loading);
- resetSelect(countrySelect, text.country);
- resetSelect(industrySelect, text.loading);
- if (countrySelect) countrySelect.disabled = true;
- try {
- const url = optionsUrl + (optionsUrl.indexOf('?') >= 0 ? '&' : '?') + 'lang=' + encodeURIComponent(lang);
- const res = await fetch(url, { headers: { 'X-Requested-With': 'XMLHttpRequest' } });
- const data = await res.json();
- if (data.code !== 1) throw new Error(data.msg || 'options failed');
- const payload = data.data || {};
- countryTree = Array.isArray(payload.country_tree) ? payload.country_tree : [];
- if (Number(payload.need_captcha || 0) === 1) {
- showCaptcha(true);
- }
- resetSelect(regionSelect, text.region);
- countryTree.forEach(function (region) {
- appendOption(regionSelect, region.value, region.title || region.name || region.value);
- });
- resetSelect(industrySelect, text.industry);
- (Array.isArray(payload.industries) ? payload.industries : []).forEach(function (item) {
- appendOption(industrySelect, item.value, item.title || item.name || item.value);
- });
- } catch (err) {
- resetSelect(regionSelect, text.region);
- resetSelect(industrySelect, text.industry);
- setMessage(text.optionsFailed, 'error');
- }
- }
- if (regionSelect) {
- regionSelect.addEventListener('change', function () {
- syncSelectState(regionSelect);
- fillCountries(regionSelect.value);
- });
- }
- [countrySelect, industrySelect].forEach(function (select) {
- if (!select) return;
- select.addEventListener('change', function () {
- syncSelectState(select);
- });
- });
- function focusField(input) {
- if (!input) return;
- input.scrollIntoView({ behavior: 'smooth', block: 'center' });
- input.focus({ preventScroll: true });
- }
- function updateContentCount() {
- if (!contentInput || !contentCount) return;
- const max = Number(contentInput.getAttribute('maxlength') || 300);
- const current = contentInput.value.length;
- contentCount.textContent = current + '/' + max;
- contentCount.style.color = current > max * 0.9 ? '#d92d20' : '#98a2b3';
- }
- function isValidEmail(value) {
- const email = String(value || '').trim();
- if (!email || email.length > 120) return false;
- return /^[A-Za-z0-9.!#$%&'*+/=?^_`{|}~-]+@[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+$/.test(email);
- }
- function isValidPhone(value) {
- const phone = String(value || '').trim();
- if (phone.length < 6 || phone.length > 40) return false;
- if (!/^[0-9+()\-\s]+$/.test(phone)) return false;
- const digits = phone.replace(/\D/g, '');
- return digits.length >= 6;
- }
- function isWithinMaxLength(input) {
- if (!input) return true;
- const max = Number(input.getAttribute('maxlength') || 0);
- return !max || String(input.value || '').length <= max;
- }
- function limitInputLength(input) {
- if (!input) return;
- const max = Number(input.getAttribute('maxlength') || 0);
- if (max > 0 && input.value.length > max) {
- input.value = input.value.slice(0, max);
- }
- }
- Array.prototype.forEach.call(form.querySelectorAll('input[maxlength], textarea[maxlength]'), function (input) {
- input.addEventListener('input', function () {
- limitInputLength(input);
- if (input === contentInput) updateContentCount();
- });
- });
- if (contentInput) {
- contentInput.addEventListener('input', updateContentCount);
- updateContentCount();
- }
- form.addEventListener('submit', async function (e) {
- e.preventDefault();
- const emailInput = form.querySelector('input[name="email"]');
- const phoneInput = form.querySelector('input[name="phone"]');
- const privacyInput = form.querySelector('input[name="privacy_agree"]');
- const email = emailInput ? emailInput.value.trim() : '';
- const phone = phoneInput ? phoneInput.value.trim() : '';
- if (!form.checkValidity() || !privacyInput || !privacyInput.checked) {
- form.reportValidity && form.reportValidity();
- setMessage(text.required, 'error');
- return;
- }
- const captchaWrap = document.getElementById('cooperationCaptchaWrap');
- const captchaInput = document.getElementById('cooperationCaptchaInput');
- if (captchaWrap && captchaWrap.style.display !== 'none' && (!captchaInput || !captchaInput.value.trim())) {
- focusField(captchaInput);
- setMessage(text.captchaHint, 'error');
- return;
- }
- if (captchaInput && captchaInput.value) captchaInput.value = captchaInput.value.trim();
- const lengthLimitedInputs = Array.prototype.slice.call(form.querySelectorAll('input[maxlength], textarea[maxlength]'));
- for (let i = 0; i < lengthLimitedInputs.length; i += 1) {
- if (!isWithinMaxLength(lengthLimitedInputs[i])) {
- focusField(lengthLimitedInputs[i]);
- setMessage(text.fieldTooLong, 'error');
- return;
- }
- }
- if (!isValidPhone(phone)) {
- focusField(phoneInput);
- setMessage(text.invalidPhone, 'error');
- return;
- }
- if (!isValidEmail(email)) {
- focusField(emailInput);
- setMessage(text.invalidEmailHint || text.invalidEmail, 'error');
- return;
- }
- if (contentInput && contentInput.value.length > 300) {
- focusField(contentInput);
- setMessage(text.tooLong, 'error');
- return;
- }
- ['name', 'phone', 'email', 'company'].forEach(function (name) {
- const input = form.querySelector('[name="' + name + '"]');
- if (input) input.value = input.value.trim();
- });
- if (contentInput) contentInput.value = contentInput.value.trim();
- if (sourcePageInput) sourcePageInput.value = window.location.href;
- if (submitBtn) submitBtn.disabled = true;
- setMessage(text.sending, 'loading');
- try {
- const body = new URLSearchParams(new FormData(form));
- 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) {
- form.reset();
- updateContentCount();
- resetSelect(countrySelect, text.country);
- if (countrySelect) {
- countrySelect.disabled = true;
- syncSelectState(countrySelect);
- }
- showCaptcha(false);
- const successMessage = data.msg || text.success;
- setMessage('', '');
- showSuccessModal(successMessage);
- } else {
- if ((data.data && Number(data.data.need_captcha || 0) === 1) || String(data.msg || '').indexOf(text.captchaError) >= 0) {
- showCaptcha(true);
- }
- setMessage(data.msg || text.failed, 'error');
- }
- } catch (err) {
- setMessage(text.failed, 'error');
- } finally {
- if (submitBtn) submitBtn.disabled = false;
- }
- });
- loadOptions();
- })();
- </script>
- {include file="footer-links" /}
- {include file="footer" /}
|