补充主题静态资源与归档模板

This commit is contained in:
kate522 committed 2026-07-03 23:15:11 +08:00
1 parent 1268a55dd0
commit 3247db7ed1
265 files changed
+59174

No files matched your search

+390
View File
@@ -0,0 +1,390 @@
/**
* AKSRT Sign/Register — 统一认证交互脚本
*
* 对齐 zibll 的 sign-register.js:
* - 短信验证码倒计时(60 秒 + 服务端 remaining_time)
* - 手机号注册/登录表单交互
* - 人机验证联动
* - 密码可见性切换
* - 协议勾选校验
*/
;(() => {
'use strict'
const COUNTDOWN_KEY = 'aksrt_sms_cooldown'
const DEFAULT_COOLDOWN = 60
const REST_BASE = '/wp-json/aksrt/v1'
/**
* 初始化所有认证页面
*/
function initAuthPages() {
initSmsButtons()
initPasswordToggles()
initAgreementChecks()
initFormValidation()
initPhoneAuthTabs()
}
/* =================================================================
* SMS 验证码发送 + 倒计时
* ================================================================= */
function initSmsButtons() {
document.querySelectorAll('[data-aksrt-sms-trigger]').forEach(btn => {
const cooldown = parseInt(btn.dataset.cooldown || DEFAULT_COOLDOWN, 10)
// 恢复页面刷新后的倒计时
const stored = sessionStorage.getItem(COUNTDOWN_KEY)
if (stored) {
const remaining = Math.ceil((parseInt(stored, 10) - Date.now()) / 1000)
if (remaining > 0) {
startCountdown(btn, remaining)
}
}
btn.addEventListener('click', e => {
e.preventDefault()
if (btn.disabled) return
sendSmsCode(btn, cooldown)
})
})
}
function sendSmsCode(btn, cooldown) {
const phoneInput = document.querySelector(btn.dataset.aksrtSmsPhone || '[data-aksrt-phone-input]')
const phone = phoneInput ? phoneInput.value.trim() : ''
if (!/^1[3-9]\d{9}$/.test(phone)) {
showToast('请输入正确的手机号', 'warning')
if (phoneInput) phoneInput.focus()
return
}
// 人机验证联动
const captchaState = window.AKSRTCaptchaState
const scene = btn.dataset.aksrtSmsScene || 'register'
if (captchaState && captchaState.scenes && captchaState.scenes[scene]) {
captchaState.scenes[scene].verify(() => {
doSendSms(btn, phone, cooldown)
})
} else {
doSendSms(btn, phone, cooldown)
}
}
function doSendSms(btn, phone, cooldown) {
btn.disabled = true
const original = btn.textContent
btn.textContent = '发送中...'
// 收集人机验证字段
const form = btn.closest('form')
const captchaData = collectCaptchaFields(form)
fetch(REST_BASE + '/auth/send-sms-code', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ phone, ...captchaData }),
})
.then(r => r.json().then(data => ({ ok: r.ok, data })))
.then(({ ok, data }) => {
if (ok && data.success) {
showToast(data.message || '验证码已发送', 'success')
const seconds = (data.data && data.data.remaining_time) || cooldown
sessionStorage.setItem(COUNTDOWN_KEY, String(Date.now() + seconds * 1000))
startCountdown(btn, seconds)
} else {
const msg = (data.data && data.data.message) || data.message || '发送失败'
showToast(msg, 'error')
const remaining = data.data && data.data.remaining_time
if (remaining && remaining > 0) {
sessionStorage.setItem(COUNTDOWN_KEY, String(Date.now() + remaining * 1000))
startCountdown(btn, remaining)
} else {
btn.disabled = false
btn.textContent = original
}
}
})
.catch(() => {
showToast('网络错误,请重试', 'error')
btn.disabled = false
btn.textContent = original
})
}
function startCountdown(btn, seconds) {
let remaining = Math.max(1, Math.floor(seconds))
btn.disabled = true
btn.textContent = remaining + 's'
const timer = setInterval(() => {
remaining--
if (remaining <= 0) {
clearInterval(timer)
btn.disabled = false
btn.textContent = '获取验证码'
sessionStorage.removeItem(COUNTDOWN_KEY)
} else {
btn.textContent = remaining + 's'
}
}, 1000)
}
/* =================================================================
* 手机号注册/登录表单
* ================================================================= */
function initPhoneAuthTabs() {
document.querySelectorAll('[data-aksrt-auth-tab]').forEach(tab => {
tab.addEventListener('click', e => {
e.preventDefault()
const target = tab.dataset.aksrtAuthTab
switchAuthTab(target)
})
})
// 手机号注册表单
const phoneRegForm = document.getElementById('aksrt-phone-register-form')
if (phoneRegForm) {
phoneRegForm.addEventListener('submit', e => {
e.preventDefault()
handlePhoneRegister(phoneRegForm)
})
}
// 手机号登录表单
const phoneLoginForm = document.getElementById('aksrt-phone-login-form')
if (phoneLoginForm) {
phoneLoginForm.addEventListener('submit', e => {
e.preventDefault()
handlePhoneLogin(phoneLoginForm)
})
}
}
function switchAuthTab(tabName) {
document.querySelectorAll('[data-auth-panel]').forEach(panel => {
panel.style.display = panel.dataset.authPanel === tabName ? '' : 'none'
})
document.querySelectorAll('[data-aksrt-auth-tab]').forEach(tab => {
tab.classList.toggle('is-active', tab.dataset.aksrtAuthTab === tabName)
})
}
function handlePhoneRegister(form) {
const phone = form.querySelector('[name="phone"]')?.value?.trim()
const code = form.querySelector('[name="code"]')?.value?.trim()
const password = form.querySelector('[name="password"]')?.value
const confirm = form.querySelector('[name="password_confirm"]')?.value
const inviteCode = form.querySelector('[name="invite_code"]')?.value?.trim()
if (!phone || !code || !password) {
showToast('请填写完整信息', 'warning')
return
}
if (password !== confirm) {
showToast('两次密码不一致', 'warning')
return
}
if (password.length < 6) {
showToast('密码至少 6 位', 'warning')
return
}
const submitBtn = form.querySelector('[type="submit"]')
if (submitBtn) {
submitBtn.disabled = true
submitBtn.textContent = '注册中...'
}
const captchaData = collectCaptchaFields(form)
fetch(REST_BASE + '/auth/phone-register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ phone, code, password, invite_code: inviteCode, ...captchaData }),
})
.then(r => r.json().then(data => ({ ok: r.ok, data })))
.then(({ ok, data }) => {
if (ok && data.success) {
showToast(data.message || '注册成功', 'success')
setTimeout(() => {
window.location.href = data.data?.redirect || '/'
}, 500)
} else {
showToast(data.message || '注册失败', 'error')
if (submitBtn) {
submitBtn.disabled = false
submitBtn.textContent = '注册'
}
}
})
.catch(() => {
showToast('网络错误', 'error')
if (submitBtn) {
submitBtn.disabled = false
submitBtn.textContent = '注册'
}
})
}
function handlePhoneLogin(form) {
const phone = form.querySelector('[name="phone"]')?.value?.trim()
const password = form.querySelector('[name="password"]')?.value
const loginMode = form.querySelector('[name="login_mode"]')?.value || 'password'
if (!phone) {
showToast('请输入手机号', 'warning')
return
}
const submitBtn = form.querySelector('[type="submit"]')
if (submitBtn) {
submitBtn.disabled = true
submitBtn.textContent = '登录中...'
}
let endpoint, body
if (loginMode === 'code') {
const code = form.querySelector('[name="code"]')?.value?.trim()
if (!code) {
showToast('请输入验证码', 'warning')
if (submitBtn) { submitBtn.disabled = false; submitBtn.textContent = '登录' }
return
}
endpoint = '/auth/phone-code-login'
body = { phone, code }
} else {
if (!password) {
showToast('请输入密码', 'warning')
if (submitBtn) { submitBtn.disabled = false; submitBtn.textContent = '登录' }
return
}
endpoint = '/auth/phone-login'
body = { phone, password }
}
fetch(REST_BASE + endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
})
.then(r => r.json().then(data => ({ ok: r.ok, data })))
.then(({ ok, data }) => {
if (ok && data.success) {
showToast('登录成功', 'success')
setTimeout(() => {
window.location.href = data.data?.redirect || '/'
}, 500)
} else {
showToast(data.message || '登录失败', 'error')
if (submitBtn) {
submitBtn.disabled = false
submitBtn.textContent = '登录'
}
}
})
.catch(() => {
showToast('网络错误', 'error')
if (submitBtn) {
submitBtn.disabled = false
submitBtn.textContent = '登录'
}
})
}
/* =================================================================
* 辅助功能
* ================================================================= */
function collectCaptchaFields(form) {
if (!form) return {}
const fields = {}
const names = [
'captcha_verified', 'tencent_ticket', 'tencent_randstr', 'tencent_sid',
'cf_turnstile_response', 'geetest_lot_number', 'geetest_captcha_output',
'geetest_pass_token', 'geetest_gen_time', 'aliyun_captcha_verify_param'
]
names.forEach(name => {
const el = form.querySelector('[name="' + name + '"]')
if (el && el.value) fields[name] = el.value
})
return fields
}
function initPasswordToggles() {
document.querySelectorAll('[data-aksrt-password-toggle]').forEach(btn => {
btn.addEventListener('click', () => {
const input = btn.closest('.input-wrap')?.querySelector('input')
if (!input) return
const isPassword = input.type === 'password'
input.type = isPassword ? 'text' : 'password'
const icon = btn.querySelector('i, svg')
if (icon) {
if (icon.tagName === 'I') {
icon.className = isPassword ? 'fas fa-eye-slash' : 'fas fa-eye'
}
}
})
})
}
function initAgreementChecks() {
document.querySelectorAll('form[data-aksrt-auth-form]').forEach(form => {
const checkbox = form.querySelector('[data-aksrt-agreement]')
const submitBtn = form.querySelector('[type="submit"]')
if (checkbox && submitBtn) {
const update = () => { submitBtn.disabled = !checkbox.checked }
checkbox.addEventListener('change', update)
update()
}
})
}
function initFormValidation() {
document.querySelectorAll('form[data-aksrt-auth-form]').forEach(form => {
form.addEventListener('submit', e => {
const password = form.querySelector('[name="password"]')
const confirm = form.querySelector('[name="password_confirm"]')
if (password && confirm && password.value !== confirm.value) {
e.preventDefault()
showToast('两次密码不一致', 'warning')
confirm.focus()
return
}
})
})
}
function showToast(message, type) {
if (typeof window.aksrtShowToast === 'function') {
window.aksrtShowToast(message, type)
return
}
const toast = document.createElement('div')
toast.className = 'aksrt-auth-toast aksrt-auth-toast--' + (type || 'info')
toast.textContent = message
document.body.appendChild(toast)
setTimeout(() => toast.classList.add('aksrt-auth-toast--show'), 10)
setTimeout(() => {
toast.classList.remove('aksrt-auth-toast--show')
setTimeout(() => toast.remove(), 300)
}, 3000)
}
// 初始化
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initAuthPages)
} else {
initAuthPages()
}
// 暴露给外部
window.AKSRTSignRegister = {
init: initAuthPages,
sendSmsCode,
switchAuthTab,
showToast,
}
})()