批量优化
This commit is contained in:
1 parent
438145cae7
commit
c925b198d1
50 files changed
+5716
-291
No files matched your search
@@ -0,0 +1,436 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
<title>统一认证中心 - 找回密码</title>
|
||||
<style>
|
||||
:root {
|
||||
--primary: #3973ff;
|
||||
--primary-dark: #2560a9;
|
||||
--text: #1f2b48;
|
||||
--text-sub: #5b6780;
|
||||
--muted: #8a94ad;
|
||||
--border: #e3e9f5;
|
||||
--radius: 12px;
|
||||
}
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
html, body { height: 100%; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
|
||||
color: var(--text);
|
||||
background: linear-gradient(120deg, #eef4ff 0%, #f7fbff 55%, #eaf3ff 100%);
|
||||
min-height: 100vh;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
padding: 32px 20px;
|
||||
position: relative; overflow-x: hidden;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
.glow { position: fixed; border-radius: 50%; filter: blur(60px); pointer-events: none; z-index: 0; }
|
||||
.glow-1 { width: 380px; height: 380px; top: -120px; left: -100px; background: rgba(82, 168, 255, 0.28); }
|
||||
.glow-2 { width: 320px; height: 320px; bottom: -120px; right: -80px; background: rgba(57, 115, 255, 0.20); }
|
||||
.auth-card {
|
||||
position: relative; z-index: 1;
|
||||
display: flex;
|
||||
width: 100%; max-width: 940px; min-height: 560px;
|
||||
background: #fff; border-radius: 22px;
|
||||
box-shadow: 0 18px 50px 0 rgba(37, 96, 169, 0.14), 0 2px 6px 0 rgba(31, 43, 72, 0.04);
|
||||
overflow: hidden;
|
||||
}
|
||||
.auth-side {
|
||||
width: 340px; flex: 0 0 340px;
|
||||
padding: 44px 34px 28px;
|
||||
background: linear-gradient(160deg, #1b3b8f 0%, #2f6fe4 52%, #52a8ff 100%);
|
||||
color: #fff;
|
||||
display: flex; flex-direction: column;
|
||||
position: relative; overflow: hidden;
|
||||
}
|
||||
.auth-side::before {
|
||||
content: ''; position: absolute; width: 260px; height: 260px;
|
||||
top: -90px; right: -110px; border-radius: 50%; background: rgba(255, 255, 255, 0.10);
|
||||
}
|
||||
.auth-side::after {
|
||||
content: ''; position: absolute; width: 190px; height: 190px;
|
||||
bottom: -70px; left: -60px; border-radius: 50%; background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
.brand { display: flex; align-items: center; gap: 12px; position: relative; z-index: 1; }
|
||||
.brand-logo {
|
||||
width: 40px; height: 40px; border-radius: 11px;
|
||||
background: rgba(255, 255, 255, 0.18); border: 1px solid rgba(255, 255, 255, 0.30);
|
||||
display: flex; align-items: center; justify-content: center; flex: 0 0 auto;
|
||||
}
|
||||
.brand-logo svg { width: 22px; height: 22px; }
|
||||
.brand-name { font-size: 16px; font-weight: 600; letter-spacing: .5px; }
|
||||
.side-title { margin-top: 44px; font-size: 26px; font-weight: 600; line-height: 1.45; position: relative; z-index: 1; }
|
||||
.side-desc {
|
||||
margin-top: 12px; font-size: 13.5px; line-height: 1.9;
|
||||
color: rgba(255, 255, 255, 0.76); position: relative; z-index: 1;
|
||||
}
|
||||
.side-illus { margin: auto 0; padding: 22px 0; position: relative; z-index: 1; }
|
||||
.side-illus svg { width: 100%; height: auto; display: block; }
|
||||
.side-foot {
|
||||
position: relative; z-index: 1;
|
||||
padding-top: 16px; border-top: 1px solid rgba(255, 255, 255, 0.16);
|
||||
font-size: 12px; color: rgba(255, 255, 255, 0.62);
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
}
|
||||
.side-foot svg { width: 13px; height: 13px; }
|
||||
.auth-main {
|
||||
flex: 1; min-width: 0;
|
||||
padding: 52px 56px 40px;
|
||||
display: flex; flex-direction: column; justify-content: center;
|
||||
}
|
||||
.panel-head { margin-bottom: 26px; }
|
||||
.panel-title { font-size: 26px; font-weight: 600; letter-spacing: .5px; }
|
||||
.panel-sub { margin-top: 9px; font-size: 13.5px; color: var(--text-sub); line-height: 1.7; }
|
||||
.error {
|
||||
display: none; align-items: flex-start; gap: 9px;
|
||||
background: #fff2f0; border: 1px solid #ffd4cf; color: #cf1322;
|
||||
padding: 11px 14px; border-radius: 10px; font-size: 13px; line-height: 1.6;
|
||||
margin-bottom: 18px; animation: shake .32s ease;
|
||||
}
|
||||
.error.show { display: flex; }
|
||||
.error svg { width: 16px; height: 16px; flex: 0 0 auto; margin-top: 2px; }
|
||||
@keyframes shake {
|
||||
0%, 100% { transform: translateX(0); }
|
||||
25% { transform: translateX(-4px); }
|
||||
75% { transform: translateX(4px); }
|
||||
}
|
||||
.field { margin-bottom: 16px; }
|
||||
.field-label { display: flex; align-items: center; justify-content: space-between; font-size: 13px; color: var(--text-sub); margin-bottom: 8px; font-weight: 500; }
|
||||
.input-wrap { position: relative; }
|
||||
.input-icon {
|
||||
position: absolute; left: 14px; top: 50%; transform: translateY(-50%);
|
||||
width: 18px; height: 18px; color: #a7b1c7; pointer-events: none; transition: color .2s;
|
||||
}
|
||||
.input-wrap input {
|
||||
width: 100%; height: 46px;
|
||||
padding: 0 44px 0 42px;
|
||||
font-size: 14px; color: var(--text);
|
||||
border: 1px solid var(--border); border-radius: var(--radius);
|
||||
background: #fbfcff; outline: none;
|
||||
transition: border-color .2s, box-shadow .2s, background .2s;
|
||||
}
|
||||
.input-wrap input::placeholder { color: #adb6c9; }
|
||||
.input-wrap input:focus {
|
||||
border-color: var(--primary); background: #fff;
|
||||
box-shadow: 0 0 0 4px rgba(57, 115, 255, 0.12);
|
||||
}
|
||||
.eye-btn {
|
||||
position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
|
||||
width: 32px; height: 32px; border: none; background: transparent; border-radius: 8px;
|
||||
color: #a7b1c7; cursor: pointer;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
transition: color .2s, background .2s;
|
||||
}
|
||||
.eye-btn:hover { color: var(--primary); background: #f0f5ff; }
|
||||
.eye-btn svg { width: 18px; height: 18px; }
|
||||
.code-btn {
|
||||
position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
|
||||
height: 32px; padding: 0 14px;
|
||||
border: none; border-radius: 8px; cursor: pointer;
|
||||
background: var(--primary); color: #fff; font-size: 12.5px;
|
||||
transition: opacity .2s, background .2s;
|
||||
}
|
||||
.code-btn:hover:not(:disabled) { background: var(--primary-dark); }
|
||||
.code-btn:disabled { background: #c3cdf0; cursor: not-allowed; }
|
||||
.link { color: var(--primary); text-decoration: none; cursor: pointer; transition: color .2s; }
|
||||
.link:hover { color: var(--primary-dark); text-decoration: underline; }
|
||||
.btn {
|
||||
width: 100%; height: 46px; border: none; border-radius: var(--radius);
|
||||
cursor: pointer;
|
||||
background: linear-gradient(135deg, #3973ff 0%, #52a8ff 100%);
|
||||
color: #fff; font-size: 15.5px; font-weight: 500; letter-spacing: 2px;
|
||||
display: flex; align-items: center; justify-content: center; gap: 8px;
|
||||
box-shadow: 0 8px 20px rgba(57, 115, 255, 0.28);
|
||||
transition: transform .15s, box-shadow .2s, opacity .2s;
|
||||
margin-top: 6px;
|
||||
}
|
||||
.btn:hover:not(:disabled) { transform: translateY(-1px); box-shadow: 0 12px 26px rgba(57, 115, 255, 0.34); }
|
||||
.btn:disabled { opacity: .72; cursor: not-allowed; box-shadow: none; }
|
||||
.spinner {
|
||||
display: none; width: 16px; height: 16px;
|
||||
border: 2px solid rgba(255, 255, 255, 0.45); border-top-color: #fff;
|
||||
border-radius: 50%; animation: spin .7s linear infinite;
|
||||
}
|
||||
.btn.is-loading .spinner { display: block; }
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
.back { text-align: center; margin-top: 22px; font-size: 13px; color: var(--text-sub); }
|
||||
.foot-tip {
|
||||
margin-top: 24px; padding-top: 16px; border-top: 1px dashed var(--border);
|
||||
font-size: 12px; color: var(--muted);
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
}
|
||||
.foot-tip svg { width: 13px; height: 13px; flex: 0 0 auto; }
|
||||
@media (max-width: 900px) {
|
||||
body { padding: 20px 14px; align-items: flex-start; }
|
||||
.auth-card { max-width: 520px; min-height: 0; flex-direction: column; }
|
||||
.auth-side { width: 100%; flex: none; padding: 30px 26px 24px; }
|
||||
.side-title { margin-top: 26px; font-size: 21px; }
|
||||
.side-illus { display: none; }
|
||||
.auth-main { padding: 34px 26px 30px; }
|
||||
.panel-title { font-size: 22px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="glow glow-1"></div>
|
||||
<div class="glow glow-2"></div>
|
||||
|
||||
<div class="auth-card">
|
||||
<aside class="auth-side">
|
||||
<div class="brand">
|
||||
<span class="brand-logo">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M12 3l7 3v6c0 4.2-2.9 7.6-7 9-4.1-1.4-7-4.8-7-9V6l7-3z"/>
|
||||
<path d="M9.2 12.2l2 2 3.6-4"/>
|
||||
</svg>
|
||||
</span>
|
||||
<span class="brand-name">云泽统一认证中心</span>
|
||||
</div>
|
||||
|
||||
<div class="side-title">找回登录密码</div>
|
||||
<div class="side-desc">
|
||||
通过注册手机号验证身份后即可重置密码;<br>
|
||||
统一认证下密码只有一份,<br>
|
||||
重置后您在所有企业的登录密码同步更新。
|
||||
</div>
|
||||
|
||||
<div class="side-illus">
|
||||
<svg viewBox="0 0 260 170" fill="none">
|
||||
<ellipse cx="130" cy="150" rx="98" ry="11" fill="rgba(255,255,255,.13)"/>
|
||||
<rect x="76" y="30" width="108" height="76" rx="12" fill="rgba(255,255,255,.14)" stroke="rgba(255,255,255,.4)" stroke-width="1.6"/>
|
||||
<path d="M104 62v-8a26 26 0 0152 0v8" stroke="rgba(255,255,255,.7)" stroke-width="4.5" stroke-linecap="round"/>
|
||||
<rect x="98" y="62" width="64" height="34" rx="8" fill="rgba(255,255,255,.3)"/>
|
||||
<circle cx="130" cy="79" r="5" fill="rgba(255,255,255,.85)"/>
|
||||
<path d="M130 82v7" stroke="rgba(255,255,255,.85)" stroke-width="3.5" stroke-linecap="round"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="side-foot">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
||||
<rect x="4" y="11" width="16" height="10" rx="2"/>
|
||||
<path d="M8 11V8a4 4 0 018 0v3"/>
|
||||
</svg>
|
||||
<span>© 2026 Yunzer · 连接已加密</span>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main class="auth-main">
|
||||
<div class="panel-head">
|
||||
<h1 class="panel-title">找回密码</h1>
|
||||
<p class="panel-sub">请输入注册手机号,通过短信验证后设置新密码</p>
|
||||
</div>
|
||||
|
||||
<div class="error" id="error">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
||||
<circle cx="12" cy="12" r="9"/><path d="M12 8v5M12 16h.01"/>
|
||||
</svg>
|
||||
<span id="errorText"></span>
|
||||
</div>
|
||||
|
||||
<form id="resetForm" onsubmit="return submitReset(event)">
|
||||
<div class="field">
|
||||
<label class="field-label" for="phone">注册手机号</label>
|
||||
<div class="input-wrap">
|
||||
<input id="phone" type="tel" placeholder="请输入注册时使用的手机号" maxlength="11" autofocus>
|
||||
<svg class="input-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="7" y="2" width="10" height="20" rx="2.5"/><path d="M11 18.5h2"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="field-label" for="code">短信验证码</label>
|
||||
<div class="input-wrap">
|
||||
<input id="code" type="text" placeholder="请输入验证码" maxlength="6" style="padding-right:110px">
|
||||
<svg class="input-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M3 8l9 6 9-6"/><rect x="3" y="5" width="18" height="14" rx="2.5"/>
|
||||
</svg>
|
||||
<button class="code-btn" type="button" id="codeBtn" onclick="sendCode()">获取验证码</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="field-label" for="password">新密码</label>
|
||||
<div class="input-wrap">
|
||||
<input id="password" type="password" placeholder="至少 6 位" autocomplete="new-password">
|
||||
<svg class="input-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="4" y="11" width="16" height="10" rx="2"/><path d="M8 11V8a4 4 0 018 0v3"/>
|
||||
</svg>
|
||||
<button class="eye-btn" type="button" onclick="togglePassword('password', this)" title="显示密码" aria-label="显示密码">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M2 12s3.6-6 10-6 10 6 10 6-3.6 6-10 6-10-6-10-6z"/><circle cx="12" cy="12" r="3"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="field-label" for="confirm">确认新密码</label>
|
||||
<div class="input-wrap">
|
||||
<input id="confirm" type="password" placeholder="请再次输入新密码" autocomplete="new-password">
|
||||
<svg class="input-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="4" y="11" width="16" height="10" rx="2"/><path d="M8 11V8a4 4 0 018 0v3"/>
|
||||
</svg>
|
||||
<button class="eye-btn" type="button" onclick="togglePassword('confirm', this)" title="显示密码" aria-label="显示密码">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M2 12s3.6-6 10-6 10 6 10 6-3.6 6-10 6-10-6-10-6z"/><circle cx="12" cy="12" r="3"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn" id="submitBtn" type="submit">
|
||||
<span class="spinner"></span>
|
||||
<span id="btnLabel">重置密码</span>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="back">想起密码了?<a class="link" id="backLogin" href="/auth/login">返回登录</a></div>
|
||||
|
||||
<div class="foot-tip">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M12 3l7 3v6c0 4.2-2.9 7.6-7 9-4.1-1.4-7-4.8-7-9V6l7-3z"/>
|
||||
</svg>
|
||||
<span>如手机号已停用,请联系企业管理员协助处理</span>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var CLIENT_ID = "{{.ClientID}}";
|
||||
var REDIRECT = "{{.Redirect}}";
|
||||
|
||||
(function initBackLink() {
|
||||
var params = new URLSearchParams();
|
||||
if (CLIENT_ID) params.set('client_id', CLIENT_ID);
|
||||
if (REDIRECT) params.set('redirect', REDIRECT);
|
||||
var qs = params.toString();
|
||||
document.getElementById('backLogin').href = '/auth/login' + (qs ? '?' + qs : '');
|
||||
})();
|
||||
|
||||
function showError(msg) {
|
||||
var box = document.getElementById('error');
|
||||
document.getElementById('errorText').textContent = msg;
|
||||
box.classList.remove('show');
|
||||
void box.offsetWidth;
|
||||
box.classList.add('show');
|
||||
}
|
||||
|
||||
function hideError() {
|
||||
var box = document.getElementById('error');
|
||||
if (box) box.classList.remove('show');
|
||||
}
|
||||
|
||||
function togglePassword(id, btn) {
|
||||
var input = document.getElementById(id);
|
||||
var show = input.type === 'password';
|
||||
input.type = show ? 'text' : 'password';
|
||||
btn.title = show ? '隐藏密码' : '显示密码';
|
||||
}
|
||||
|
||||
var codeTimer = null;
|
||||
function setCodeBtnCountdown(sec) {
|
||||
var btn = document.getElementById('codeBtn');
|
||||
if (codeTimer) { clearInterval(codeTimer); codeTimer = null; }
|
||||
btn.disabled = true;
|
||||
var left = sec;
|
||||
btn.textContent = left + 's 后重发';
|
||||
codeTimer = setInterval(function () {
|
||||
left--;
|
||||
if (left <= 0) {
|
||||
clearInterval(codeTimer); codeTimer = null;
|
||||
btn.disabled = false;
|
||||
btn.textContent = '重新获取';
|
||||
} else {
|
||||
btn.textContent = left + 's 后重发';
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function sendCode() {
|
||||
hideError();
|
||||
var phone = document.getElementById('phone').value.trim();
|
||||
if (!/^1[3-9]\d{9}$/.test(phone)) { showError('请输入正确的手机号'); return; }
|
||||
|
||||
var btn = document.getElementById('codeBtn');
|
||||
btn.disabled = true;
|
||||
btn.textContent = '发送中...';
|
||||
|
||||
fetch('/auth/forgot/send-code', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ phone: phone })
|
||||
})
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (res) {
|
||||
if (res.code !== 200) {
|
||||
showError(res.msg || '验证码发送失败');
|
||||
btn.disabled = false;
|
||||
btn.textContent = '重新获取';
|
||||
return;
|
||||
}
|
||||
setCodeBtnCountdown(60);
|
||||
})
|
||||
.catch(function () {
|
||||
showError('网络异常,请稍后重试');
|
||||
btn.disabled = false;
|
||||
btn.textContent = '重新获取';
|
||||
});
|
||||
}
|
||||
|
||||
function submitReset(e) {
|
||||
e.preventDefault();
|
||||
hideError();
|
||||
|
||||
var phone = document.getElementById('phone').value.trim();
|
||||
var code = document.getElementById('code').value.trim();
|
||||
var password = document.getElementById('password').value;
|
||||
var confirm = document.getElementById('confirm').value;
|
||||
|
||||
if (!/^1[3-9]\d{9}$/.test(phone)) { showError('请输入正确的手机号'); return false; }
|
||||
if (!code) { showError('请输入短信验证码'); return false; }
|
||||
if (password.length < 6) { showError('密码长度不能少于 6 位'); return false; }
|
||||
if (password !== confirm) { showError('两次输入的密码不一致'); return false; }
|
||||
|
||||
var btn = document.getElementById('submitBtn');
|
||||
btn.disabled = true;
|
||||
btn.classList.add('is-loading');
|
||||
document.getElementById('btnLabel').textContent = '提交中...';
|
||||
|
||||
fetch('/auth/forgot/reset', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
phone: phone,
|
||||
code: code,
|
||||
password: password,
|
||||
confirm: confirm,
|
||||
client_id: CLIENT_ID,
|
||||
redirect: REDIRECT
|
||||
})
|
||||
})
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (res) {
|
||||
if (res.code !== 200) {
|
||||
showError(res.msg || '密码重置失败');
|
||||
btn.disabled = false;
|
||||
btn.classList.remove('is-loading');
|
||||
document.getElementById('btnLabel').textContent = '重置密码';
|
||||
return;
|
||||
}
|
||||
window.location.href = res.redirect || ('/auth/login?client_id=' + encodeURIComponent(CLIENT_ID));
|
||||
})
|
||||
.catch(function () {
|
||||
showError('网络异常,请稍后重试');
|
||||
btn.disabled = false;
|
||||
btn.classList.remove('is-loading');
|
||||
document.getElementById('btnLabel').textContent = '重置密码';
|
||||
});
|
||||
return false;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -164,6 +164,17 @@
|
||||
75% { transform: translateX(4px); }
|
||||
}
|
||||
|
||||
.notice-box {
|
||||
display: flex; align-items: flex-start; gap: 9px;
|
||||
background: #f0fff4; border: 1px solid #b7ebc2; color: #1f7a37;
|
||||
padding: 11px 14px; border-radius: 10px; font-size: 13px; line-height: 1.6;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.notice-box.warn {
|
||||
background: #fffbe6; border-color: #ffe89e; color: #a96a00;
|
||||
}
|
||||
.notice-box svg { width: 16px; height: 16px; flex: 0 0 auto; margin-top: 2px; }
|
||||
|
||||
.field { margin-bottom: 18px; }
|
||||
.field-label {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
@@ -455,6 +466,17 @@
|
||||
<p class="panel-sub">请输入您的账号与密码以继续</p>
|
||||
</div>
|
||||
|
||||
{{if .Notice}}
|
||||
<div class="notice-box{{if eq .Notice "registered_warn"}} warn{{end}}">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
{{if eq .Notice "registered_warn"}}<circle cx="12" cy="12" r="9"/><path d="M12 8v5M12 16h.01"/>{{else}}<path d="M12 3l7 3v6c0 4.2-2.9 7.6-7 9-4.1-1.4-7-4.8-7-9V6l7-3z"/><path d="M9.2 12.2l2 2 3.6-4"/>{{end}}
|
||||
</svg>
|
||||
<span>
|
||||
{{if eq .Notice "reset"}}密码已重置,请使用新密码登录{{else if eq .Notice "registered_warn"}}{{.WarnMsg}},可先登录{{else}}注册成功,请使用新账号登录{{end}}
|
||||
</span>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<div class="error" id="error">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
||||
<circle cx="12" cy="12" r="9"/><path d="M12 8v5M12 16h.01"/>
|
||||
@@ -493,6 +515,7 @@
|
||||
|
||||
<div class="row-extra">
|
||||
<label class="remember"><input type="checkbox" id="remember"> 记住账号</label>
|
||||
<a class="link" href="/auth/forgot?client_id={{urlquery .ClientID}}&redirect={{urlquery .Redirect}}">忘记密码?</a>
|
||||
</div>
|
||||
|
||||
<button class="btn" id="submitBtn" type="submit">
|
||||
@@ -501,6 +524,8 @@
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="back" style="margin-top:16px">还没有账号?<a class="link" href="/auth/register?client_id={{urlquery .ClientID}}&redirect={{urlquery .Redirect}}">立即注册</a></div>
|
||||
|
||||
<div class="third-area" id="thirdArea" style="display:none">
|
||||
<div class="divider"><span>其他登录方式</span></div>
|
||||
<div class="third-list" id="thirdList"></div>
|
||||
|
||||
@@ -0,0 +1,571 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
<title>统一认证中心 - 注册</title>
|
||||
<style>
|
||||
:root {
|
||||
--primary: #3973ff;
|
||||
--primary-dark: #2560a9;
|
||||
--text: #1f2b48;
|
||||
--text-sub: #5b6780;
|
||||
--muted: #8a94ad;
|
||||
--border: #e3e9f5;
|
||||
--radius: 12px;
|
||||
}
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
html, body { height: 100%; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
|
||||
color: var(--text);
|
||||
background: linear-gradient(120deg, #eef4ff 0%, #f7fbff 55%, #eaf3ff 100%);
|
||||
min-height: 100vh;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
padding: 32px 20px;
|
||||
position: relative; overflow-x: hidden;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
.glow { position: fixed; border-radius: 50%; filter: blur(60px); pointer-events: none; z-index: 0; }
|
||||
.glow-1 { width: 380px; height: 380px; top: -120px; left: -100px; background: rgba(82, 168, 255, 0.28); }
|
||||
.glow-2 { width: 320px; height: 320px; bottom: -120px; right: -80px; background: rgba(57, 115, 255, 0.20); }
|
||||
.auth-card {
|
||||
position: relative; z-index: 1;
|
||||
width: 100%; max-width: 860px;
|
||||
background: #fff; border-radius: 22px;
|
||||
box-shadow: 0 18px 50px 0 rgba(37, 96, 169, 0.14), 0 2px 6px 0 rgba(31, 43, 72, 0.04);
|
||||
padding: 40px 48px 32px;
|
||||
}
|
||||
.brand { text-align: center; margin-bottom: 22px; }
|
||||
.brand h1 { font-size: 22px; font-weight: 700; letter-spacing: 1px; }
|
||||
.brand p { color: var(--muted); font-size: 13px; margin-top: 6px; }
|
||||
|
||||
/* 步骤条 */
|
||||
.steps { display: flex; align-items: center; justify-content: center; gap: 10px; margin-bottom: 28px; }
|
||||
.step { display: flex; align-items: center; gap: 8px; color: var(--muted); font-size: 14px; }
|
||||
.step .dot {
|
||||
width: 26px; height: 26px; border-radius: 50%;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
background: #eef2fb; color: var(--muted); font-size: 13px; font-weight: 600;
|
||||
transition: all .25s;
|
||||
}
|
||||
.step.active { color: var(--text); font-weight: 600; }
|
||||
.step.active .dot { background: var(--primary); color: #fff; }
|
||||
.step.done .dot { background: #34b96f; color: #fff; }
|
||||
.step-line { width: 70px; height: 2px; background: var(--border); border-radius: 2px; }
|
||||
|
||||
.panel { display: none; }
|
||||
.panel.active { display: block; animation: fade .25s ease; }
|
||||
@keyframes fade { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
|
||||
|
||||
/* 第一步:方式切换 */
|
||||
.mode-tabs { display: flex; background: #f2f5fb; border-radius: 12px; padding: 5px; margin-bottom: 22px; }
|
||||
.mode-tab {
|
||||
flex: 1; text-align: center; padding: 10px 0; border-radius: 9px;
|
||||
font-size: 14px; color: var(--text-sub); cursor: pointer; user-select: none;
|
||||
transition: all .2s;
|
||||
}
|
||||
.mode-tab.on { background: #fff; color: var(--primary); font-weight: 600; box-shadow: 0 2px 8px rgba(31,43,72,.08); }
|
||||
.mode-pane { display: none; }
|
||||
.mode-pane.on { display: block; }
|
||||
|
||||
.field { margin-bottom: 16px; }
|
||||
.field label { display: block; font-size: 13px; color: var(--text-sub); margin-bottom: 6px; }
|
||||
.field label .req { color: #e5484d; margin-left: 2px; }
|
||||
.input-row { display: flex; gap: 10px; }
|
||||
.ipt {
|
||||
flex: 1; width: 100%; height: 44px; padding: 0 14px;
|
||||
border: 1px solid var(--border); border-radius: 10px;
|
||||
font-size: 14px; color: var(--text); outline: none; background: #fbfcff;
|
||||
transition: border-color .2s, box-shadow .2s;
|
||||
}
|
||||
.ipt:focus { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(57,115,255,.12); background: #fff; }
|
||||
.btn {
|
||||
height: 44px; padding: 0 20px; border: none; border-radius: 10px;
|
||||
font-size: 14px; cursor: pointer; transition: all .2s;
|
||||
background: #eef2fb; color: var(--text-sub);
|
||||
}
|
||||
.btn:hover { background: #e2e9f8; }
|
||||
.btn:disabled { opacity: .55; cursor: not-allowed; }
|
||||
.btn-primary {
|
||||
width: 100%; height: 46px; margin-top: 6px;
|
||||
background: linear-gradient(135deg, #3973ff, #5a8bff);
|
||||
color: #fff; font-size: 15px; font-weight: 600;
|
||||
box-shadow: 0 8px 20px -6px rgba(57,115,255,.5);
|
||||
}
|
||||
.btn-primary:hover { filter: brightness(1.05); }
|
||||
.btn-ghost { background: #fff; border: 1px solid var(--border); color: var(--text-sub); }
|
||||
.btn-ghost:hover { border-color: var(--primary); color: var(--primary); background: #fff; }
|
||||
.code-btn { min-width: 118px; }
|
||||
.hint { font-size: 12px; color: var(--muted); margin-top: 4px; line-height: 1.6; }
|
||||
|
||||
/* 账号注册二维码区 */
|
||||
.wechat-box { display: none; text-align: center; margin-bottom: 16px; padding: 16px; border: 1px dashed var(--border); border-radius: 12px; background: #fbfcff; }
|
||||
.wechat-box.show { display: block; }
|
||||
.wechat-box img { width: 168px; height: 168px; object-fit: contain; }
|
||||
.wechat-box .scan-tip { font-size: 13px; color: var(--text-sub); margin-top: 8px; }
|
||||
.wechat-box .scan-tip.ok { color: #34b96f; font-weight: 600; }
|
||||
.wechat-box .mp-notice { font-size: 12px; color: #d08700; margin-top: 6px; }
|
||||
|
||||
/* 第二步:两个卡片 */
|
||||
.cards { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
|
||||
@media (max-width: 760px) { .cards { grid-template-columns: 1fr; } .auth-card { padding: 28px 20px; } }
|
||||
.choice-card {
|
||||
border: 1px solid var(--border); border-radius: 14px; padding: 20px;
|
||||
background: #fbfcff; transition: border-color .2s, box-shadow .2s;
|
||||
}
|
||||
.choice-card:hover { border-color: rgba(57,115,255,.45); box-shadow: 0 8px 24px -10px rgba(57,115,255,.25); }
|
||||
.choice-card h3 { font-size: 15px; display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
|
||||
.choice-card .desc { font-size: 12px; color: var(--muted); margin-bottom: 14px; line-height: 1.6; }
|
||||
.choice-card .field { margin-bottom: 12px; }
|
||||
.search-list { max-height: 168px; overflow-y: auto; border: 1px solid var(--border); border-radius: 10px; margin-top: 8px; background: #fff; }
|
||||
.search-item { padding: 10px 12px; font-size: 13px; cursor: pointer; border-bottom: 1px solid #f0f3fa; display: flex; justify-content: space-between; gap: 8px; }
|
||||
.search-item:last-child { border-bottom: none; }
|
||||
.search-item:hover { background: #f4f8ff; }
|
||||
.search-item.sel { background: #ecf2ff; color: var(--primary); font-weight: 600; }
|
||||
.search-item .code { color: var(--muted); font-size: 12px; }
|
||||
.search-empty { padding: 12px; font-size: 12px; color: var(--muted); text-align: center; }
|
||||
.file-row { display: flex; align-items: center; gap: 10px; }
|
||||
.file-name { font-size: 12px; color: var(--text-sub); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.file-name.ok { color: #34b96f; }
|
||||
.card-submit { width: 100%; margin-top: 4px; }
|
||||
|
||||
.skip-row { text-align: center; margin-top: 20px; }
|
||||
.skip-link { font-size: 13px; color: var(--muted); cursor: pointer; background: none; border: none; text-decoration: underline; }
|
||||
.skip-link:hover { color: var(--primary); }
|
||||
|
||||
.msg { display: none; margin-bottom: 16px; padding: 10px 14px; border-radius: 10px; font-size: 13px; line-height: 1.6; }
|
||||
.msg.show { display: block; }
|
||||
.msg-err { background: #fdf0f0; color: #d54941; border: 1px solid #f5d4d2; }
|
||||
.msg-ok { background: #eefaf2; color: #2e9e5b; border: 1px solid #cdeeda; }
|
||||
|
||||
.foot { text-align: center; margin-top: 22px; font-size: 13px; color: var(--muted); }
|
||||
.foot a { color: var(--primary); text-decoration: none; }
|
||||
.foot a:hover { text-decoration: underline; }
|
||||
|
||||
/* 成功浮层 */
|
||||
.done-mask { display: none; position: fixed; inset: 0; background: rgba(23,32,54,.45); z-index: 50; align-items: center; justify-content: center; }
|
||||
.done-mask.show { display: flex; }
|
||||
.done-box { width: 380px; max-width: 92vw; background: #fff; border-radius: 18px; padding: 32px 28px; text-align: center; }
|
||||
.done-box .icon { width: 56px; height: 56px; border-radius: 50%; background: #eefaf2; color: #34b96f; font-size: 28px; display: flex; align-items: center; justify-content: center; margin: 0 auto 14px; }
|
||||
.done-box h3 { font-size: 17px; margin-bottom: 8px; }
|
||||
.done-box p { font-size: 13px; color: var(--text-sub); line-height: 1.7; margin-bottom: 18px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="glow glow-1"></div>
|
||||
<div class="glow glow-2"></div>
|
||||
|
||||
<div class="auth-card" id="app"
|
||||
data-client-id="{{.ClientID}}"
|
||||
data-redirect="{{.Redirect}}">
|
||||
<div class="brand">
|
||||
<h1>创建账号</h1>
|
||||
<p>注册统一认证账号,一个账号密码通行所有企业</p>
|
||||
</div>
|
||||
|
||||
<div class="steps">
|
||||
<div class="step active" id="stepDot1"><span class="dot">1</span><span>核验身份</span></div>
|
||||
<div class="step-line"></div>
|
||||
<div class="step" id="stepDot2"><span class="dot">2</span><span>企业归属</span></div>
|
||||
</div>
|
||||
|
||||
<div class="msg msg-err" id="globalMsg"></div>
|
||||
|
||||
<!-- ================= 第一步:核验身份 ================= -->
|
||||
<div class="panel active" id="panelStep1">
|
||||
<div class="mode-tabs">
|
||||
<div class="mode-tab on" data-mode="phone">手机号注册</div>
|
||||
<div class="mode-tab" data-mode="account">账号注册</div>
|
||||
</div>
|
||||
|
||||
<!-- 手机号注册 -->
|
||||
<div class="mode-pane on" id="panePhone">
|
||||
<div class="field">
|
||||
<label>手机号<span class="req">*</span></label>
|
||||
<input class="ipt" id="phone" type="tel" maxlength="11" placeholder="请输入手机号(作为登录账号)">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>短信验证码<span class="req">*</span></label>
|
||||
<div class="input-row">
|
||||
<input class="ipt" id="phoneCode" maxlength="6" placeholder="请输入短信验证码">
|
||||
<button class="btn code-btn" id="sendPhoneCode" type="button">获取验证码</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>设置密码<span class="req">*</span></label>
|
||||
<input class="ipt" id="phonePwd" type="password" placeholder="密码长度不少于6位">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>确认密码<span class="req">*</span></label>
|
||||
<input class="ipt" id="phonePwd2" type="password" placeholder="请再次输入密码">
|
||||
</div>
|
||||
<button class="btn btn-primary" id="btnVerifyPhone" type="button">验证并下一步</button>
|
||||
</div>
|
||||
|
||||
<!-- 账号注册 -->
|
||||
<div class="mode-pane" id="paneAccount">
|
||||
<div class="field">
|
||||
<label>账号名<span class="req">*</span></label>
|
||||
<input class="ipt" id="account" maxlength="32" placeholder="4-32位,字母开头,可含数字、下划线、中划线">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>设置密码<span class="req">*</span></label>
|
||||
<input class="ipt" id="accountPwd" type="password" placeholder="密码长度不少于6位">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>确认密码<span class="req">*</span></label>
|
||||
<input class="ipt" id="accountPwd2" type="password" placeholder="请再次输入密码">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>微信身份核验<span class="req">*</span></label>
|
||||
<div class="wechat-box" id="wechatBox">
|
||||
<img id="wechatQr" alt="公众号二维码" style="display:none">
|
||||
<div class="scan-tip" id="scanTip">正在获取二维码…</div>
|
||||
<div class="mp-notice" id="mpNotice"></div>
|
||||
</div>
|
||||
<div class="input-row">
|
||||
<input class="ipt" id="accountCode" maxlength="6" placeholder="公众号下发的 6 位验证码">
|
||||
<button class="btn code-btn" id="btnQr" type="button">获取二维码</button>
|
||||
</div>
|
||||
<div class="hint">用微信扫码关注公众号后,验证码将通过公众号自动发送;无法扫码时可在公众号内直接发送「注册」获取验证码。</div>
|
||||
</div>
|
||||
<button class="btn btn-primary" id="btnVerifyAccount" type="button">验证并下一步</button>
|
||||
</div>
|
||||
|
||||
<div class="foot">已有账号?<a href="/auth/login" id="toLogin">直接登录</a> · <a href="/auth/forgot" id="toForgot">忘记密码</a></div>
|
||||
</div>
|
||||
|
||||
<!-- ================= 第二步:企业归属 ================= -->
|
||||
<div class="panel" id="panelStep2">
|
||||
<div class="cards">
|
||||
<!-- 卡片一:绑定企业 -->
|
||||
<div class="choice-card">
|
||||
<h3>🏢 绑定企业</h3>
|
||||
<div class="desc">你的账号已由企业创建/企业管理员已开通企业,搜索并申请加入,由企业管理员审核。</div>
|
||||
<div class="field">
|
||||
<label>企业名称 / 编码<span class="req">*</span></label>
|
||||
<div class="input-row">
|
||||
<input class="ipt" id="tenantKeyword" placeholder="输入企业名称或编码搜索">
|
||||
<button class="btn" id="btnSearchTenant" type="button">搜索</button>
|
||||
</div>
|
||||
<div class="search-list" id="tenantList" style="display:none"></div>
|
||||
</div>
|
||||
<button class="btn btn-primary card-submit" id="btnJoin" type="button" disabled>确定加入</button>
|
||||
</div>
|
||||
|
||||
<!-- 卡片二:创建企业 -->
|
||||
<div class="choice-card">
|
||||
<h3>🏗️ 创建企业</h3>
|
||||
<div class="desc">没有现成企业?填写企业资料创建新企业,由平台审核,通过后你将成为企业管理员。</div>
|
||||
<div class="field"><label>公司名称<span class="req">*</span></label><input class="ipt" id="cName" maxlength="128" placeholder="营业执照上的公司全称"></div>
|
||||
<div class="field"><label>统一社会信用代码<span class="req">*</span></label><input class="ipt" id="cCredit" maxlength="18" placeholder="18 位统一社会信用代码"></div>
|
||||
<div class="field"><label>法定代表人姓名<span class="req">*</span></label><input class="ipt" id="cLegal" maxlength="64" placeholder="法定代表人姓名"></div>
|
||||
<div class="field">
|
||||
<label>营业执照电子档<span class="req">*</span>(PDF / JPG / PNG,≤5MB)</label>
|
||||
<div class="file-row">
|
||||
<button class="btn btn-ghost" id="btnPickFile" type="button">选择文件</button>
|
||||
<span class="file-name" id="licenseName">未选择文件</span>
|
||||
</div>
|
||||
<input type="file" id="licenseFile" accept=".pdf,.jpg,.jpeg,.png" style="display:none">
|
||||
</div>
|
||||
<div class="field"><label>联系人<span class="req">*</span></label><input class="ipt" id="cContact" maxlength="64" placeholder="联系人姓名"></div>
|
||||
<div class="field"><label>联系电话<span class="req">*</span></label><input class="ipt" id="cPhone" maxlength="11" placeholder="联系电话"></div>
|
||||
<div class="field"><label>电子邮箱<span class="req">*</span></label><input class="ipt" id="cEmail" maxlength="128" placeholder="电子邮箱"></div>
|
||||
<div class="field"><label>公司地址<span class="req">*</span></label><input class="ipt" id="cAddress" maxlength="255" placeholder="公司地址"></div>
|
||||
<button class="btn btn-primary card-submit" id="btnCreate" type="button">确定创建</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="skip-row">
|
||||
<button class="skip-link" id="btnSkip" type="button">暂不加入企业,直接完成注册 →</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 成功浮层 -->
|
||||
<div class="done-mask" id="doneMask">
|
||||
<div class="done-box">
|
||||
<div class="icon">✓</div>
|
||||
<h3 id="doneTitle">注册成功</h3>
|
||||
<p id="doneText"></p>
|
||||
<button class="btn btn-primary" id="btnGoLogin" type="button" style="width:100%">前往登录</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
var app = document.getElementById('app');
|
||||
var CLIENT_ID = app.getAttribute('data-client-id') || '';
|
||||
var REDIRECT = app.getAttribute('data-redirect') || '';
|
||||
|
||||
var state = { mode: 'phone', token: '', scene: '', qrTimer: null, selectedTid: 0 };
|
||||
|
||||
// ---------- 工具 ----------
|
||||
function $(id) { return document.getElementById(id); }
|
||||
function showMsg(text, ok) {
|
||||
var el = $('globalMsg');
|
||||
el.className = 'msg show ' + (ok ? 'msg-ok' : 'msg-err');
|
||||
el.textContent = text;
|
||||
if (!ok) { clearTimeout(showMsg._t); showMsg._t = setTimeout(function(){ el.className = 'msg'; }, 5000); }
|
||||
}
|
||||
function post(url, data, cb) {
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data || {})
|
||||
}).then(function (r) { return r.json(); }).then(function (res) { cb(res); })
|
||||
.catch(function () { cb({ code: 500, msg: '网络异常,请稍后重试' }); });
|
||||
}
|
||||
function buildLoginURL(redirect) {
|
||||
var qs = [];
|
||||
if (CLIENT_ID) qs.push('client_id=' + encodeURIComponent(CLIENT_ID));
|
||||
if (REDIRECT) qs.push('redirect=' + encodeURIComponent(REDIRECT));
|
||||
var warn = '';
|
||||
try { var u = new URL(redirect || '/auth/login', location.origin); warn = u.searchParams.get('warn') || ''; } catch (e) {}
|
||||
if (warn) qs.push('warn=' + encodeURIComponent(warn));
|
||||
return (qs.length ? '/auth/login?' + qs.join('&') : '/auth/login');
|
||||
}
|
||||
function goStep(n) {
|
||||
$('panelStep1').classList.toggle('active', n === 1);
|
||||
$('panelStep2').classList.toggle('active', n === 2);
|
||||
$('stepDot1').className = 'step ' + (n === 1 ? 'active' : 'done');
|
||||
$('stepDot1').querySelector('.dot').textContent = n === 1 ? '1' : '✓';
|
||||
$('stepDot2').className = 'step ' + (n === 2 ? 'active' : '');
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
function done(title, text, redirect) {
|
||||
$('doneTitle').textContent = title;
|
||||
$('doneText').textContent = text;
|
||||
$('btnGoLogin').onclick = function () { location.href = redirect || buildLoginURL(''); };
|
||||
$('doneMask').classList.add('show');
|
||||
}
|
||||
|
||||
// ---------- 第一步:方式切换 ----------
|
||||
document.querySelectorAll('.mode-tab').forEach(function (tab) {
|
||||
tab.addEventListener('click', function () {
|
||||
document.querySelectorAll('.mode-tab').forEach(function (t) { t.classList.remove('on'); });
|
||||
tab.classList.add('on');
|
||||
state.mode = tab.getAttribute('data-mode');
|
||||
$('panePhone').classList.toggle('on', state.mode === 'phone');
|
||||
$('paneAccount').classList.toggle('on', state.mode === 'account');
|
||||
});
|
||||
});
|
||||
|
||||
// ---------- 手机号注册 ----------
|
||||
var sendBtn = $('sendPhoneCode'), sendTimer = null;
|
||||
sendBtn.addEventListener('click', function () {
|
||||
var phone = $('phone').value.trim();
|
||||
if (!/^1[3-9]\d{9}$/.test(phone)) { showMsg('请输入正确的手机号'); return; }
|
||||
sendBtn.disabled = true;
|
||||
post('/auth/register/send-code', { phone: phone }, function (res) {
|
||||
if (res.code !== 200) {
|
||||
showMsg(res.msg || '验证码发送失败');
|
||||
sendBtn.disabled = false;
|
||||
return;
|
||||
}
|
||||
showMsg('验证码已发送,请查收短信', true);
|
||||
var sec = 60;
|
||||
sendBtn.textContent = sec + 's 后重发';
|
||||
sendTimer = setInterval(function () {
|
||||
sec--;
|
||||
if (sec <= 0) { clearInterval(sendTimer); sendBtn.textContent = '获取验证码'; sendBtn.disabled = false; }
|
||||
else { sendBtn.textContent = sec + 's 后重发'; }
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
|
||||
$('btnVerifyPhone').addEventListener('click', function () {
|
||||
var phone = $('phone').value.trim(), code = $('phoneCode').value.trim();
|
||||
var pwd = $('phonePwd').value, pwd2 = $('phonePwd2').value;
|
||||
if (!/^1[3-9]\d{9}$/.test(phone)) { showMsg('请输入正确的手机号'); return; }
|
||||
if (!code) { showMsg('请输入短信验证码'); return; }
|
||||
if (pwd.length < 6) { showMsg('密码长度不能少于6位'); return; }
|
||||
if (pwd !== pwd2) { showMsg('两次输入的密码不一致'); return; }
|
||||
var btn = this; btn.disabled = true;
|
||||
post('/auth/register/verify', { mode: 'phone', phone: phone, code: code, password: pwd, confirm: pwd2 }, function (res) {
|
||||
btn.disabled = false;
|
||||
if (res.code !== 200) { showMsg(res.msg || '验证失败'); return; }
|
||||
state.token = res.data.register_token;
|
||||
showMsg('身份核验通过', true);
|
||||
goStep(2);
|
||||
});
|
||||
});
|
||||
|
||||
// ---------- 账号注册 ----------
|
||||
$('btnQr').addEventListener('click', function () {
|
||||
var btn = this; btn.disabled = true;
|
||||
$('wechatBox').classList.add('show');
|
||||
$('wechatQr').style.display = 'none';
|
||||
$('scanTip').textContent = '正在获取二维码…';
|
||||
$('scanTip').className = 'scan-tip';
|
||||
$('mpNotice').textContent = '';
|
||||
fetch('/auth/register/wechat/start').then(function (r) { return r.json(); }).then(function (res) {
|
||||
btn.disabled = false;
|
||||
if (res.code !== 200) { $('scanTip').textContent = res.msg || '获取二维码失败'; return; }
|
||||
state.scene = res.data.scene || '';
|
||||
if (res.data.qr_url) {
|
||||
$('wechatQr').src = res.data.qr_url;
|
||||
$('wechatQr').style.display = '';
|
||||
$('scanTip').textContent = '请用微信扫码并关注公众号';
|
||||
} else {
|
||||
$('scanTip').textContent = '无法展示二维码:请在公众号内直接发送「注册」获取验证码';
|
||||
}
|
||||
if (res.data.notice) { $('mpNotice').textContent = res.data.notice; }
|
||||
startQrPolling();
|
||||
}).catch(function () { btn.disabled = false; $('scanTip').textContent = '网络异常,请重试'; });
|
||||
});
|
||||
|
||||
function startQrPolling() {
|
||||
if (state.qrTimer) clearInterval(state.qrTimer);
|
||||
if (!state.scene) return;
|
||||
state.qrTimer = setInterval(function () {
|
||||
fetch('/auth/register/wechat/status?scene=' + encodeURIComponent(state.scene))
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (res) {
|
||||
if (res.code === 200 && res.data && res.data.scanned) {
|
||||
$('scanTip').textContent = '已扫码,验证码已发送到公众号,请填入下方输入框';
|
||||
$('scanTip').className = 'scan-tip ok';
|
||||
clearInterval(state.qrTimer);
|
||||
}
|
||||
}).catch(function () {});
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
$('btnVerifyAccount').addEventListener('click', function () {
|
||||
var account = $('account').value.trim(), code = $('accountCode').value.trim();
|
||||
var pwd = $('accountPwd').value, pwd2 = $('accountPwd2').value;
|
||||
if (!/^[A-Za-z][A-Za-z0-9_-]{3,31}$/.test(account)) { showMsg('账号需 4-32 位,字母开头,仅含字母/数字/下划线/中划线'); return; }
|
||||
if (pwd.length < 6) { showMsg('密码长度不能少于6位'); return; }
|
||||
if (pwd !== pwd2) { showMsg('两次输入的密码不一致'); return; }
|
||||
if (!code) { showMsg('请输入公众号下发的验证码'); return; }
|
||||
var btn = this; btn.disabled = true;
|
||||
post('/auth/register/verify', { mode: 'account', account: account, scene: state.scene, code: code, password: pwd, confirm: pwd2 }, function (res) {
|
||||
btn.disabled = false;
|
||||
if (res.code !== 200) { showMsg(res.msg || '验证失败'); return; }
|
||||
state.token = res.data.register_token;
|
||||
if (state.qrTimer) clearInterval(state.qrTimer);
|
||||
showMsg('身份核验通过', true);
|
||||
goStep(2);
|
||||
});
|
||||
});
|
||||
|
||||
// ---------- 第二步:绑定企业 ----------
|
||||
$('btnSearchTenant').addEventListener('click', function () {
|
||||
var kw = $('tenantKeyword').value.trim();
|
||||
if (!kw) { showMsg('请输入企业名称或编码'); return; }
|
||||
var list = $('tenantList');
|
||||
list.style.display = '';
|
||||
list.innerHTML = '<div class="search-empty">搜索中…</div>';
|
||||
state.selectedTid = 0;
|
||||
$('btnJoin').disabled = true;
|
||||
fetch('/auth/register/tenant/search?keyword=' + encodeURIComponent(kw))
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (res) {
|
||||
if (res.code !== 200 || !res.data || !res.data.list || !res.data.list.length) {
|
||||
list.innerHTML = '<div class="search-empty">未找到匹配企业,请确认名称或编码</div>';
|
||||
return;
|
||||
}
|
||||
list.innerHTML = '';
|
||||
res.data.list.forEach(function (t) {
|
||||
var item = document.createElement('div');
|
||||
item.className = 'search-item';
|
||||
item.innerHTML = '<span>' + t.tenant_name.replace(/</g, '<') + '</span><span class="code">' + t.tenant_code + '</span>';
|
||||
item.addEventListener('click', function () {
|
||||
list.querySelectorAll('.search-item').forEach(function (x) { x.classList.remove('sel'); });
|
||||
item.classList.add('sel');
|
||||
state.selectedTid = t.tid;
|
||||
$('btnJoin').disabled = false;
|
||||
});
|
||||
list.appendChild(item);
|
||||
});
|
||||
}).catch(function () { list.innerHTML = '<div class="search-empty">搜索失败,请重试</div>'; });
|
||||
});
|
||||
|
||||
$('btnJoin').addEventListener('click', function () {
|
||||
if (!state.selectedTid) { showMsg('请先选择企业'); return; }
|
||||
var btn = this; btn.disabled = true;
|
||||
post('/auth/register/apply-join', { token: state.token, tid: state.selectedTid }, function (res) {
|
||||
btn.disabled = false;
|
||||
if (res.code !== 200) { showMsg(res.msg || '提交失败'); return; }
|
||||
done('申请已提交', '加入企业的申请已提交,请等待企业管理员在后台审核,审核通过后即可登录。', res.redirect || buildLoginURL(''));
|
||||
});
|
||||
});
|
||||
|
||||
// ---------- 第二步:创建企业 ----------
|
||||
$('btnPickFile').addEventListener('click', function () { $('licenseFile').click(); });
|
||||
$('licenseFile').addEventListener('change', function () {
|
||||
var f = this.files && this.files[0];
|
||||
if (!f) return;
|
||||
if (!/\.(pdf|jpg|jpeg|png)$/i.test(f.name)) { showMsg('仅支持 PDF、JPG、PNG 格式'); this.value = ''; return; }
|
||||
if (f.size > 5 * 1024 * 1024) { showMsg('文件大小不能超过 5MB'); this.value = ''; return; }
|
||||
var nameEl = $('licenseName');
|
||||
nameEl.textContent = '上传中:' + f.name;
|
||||
nameEl.className = 'file-name';
|
||||
var fd = new FormData();
|
||||
fd.append('file', f);
|
||||
fetch('/auth/register/upload-license', { method: 'POST', body: fd })
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (res) {
|
||||
if (res.code !== 200) { nameEl.textContent = '未选择文件'; showMsg(res.msg || '上传失败'); return; }
|
||||
nameEl.textContent = '已上传:' + res.data.name;
|
||||
nameEl.className = 'file-name ok';
|
||||
nameEl.setAttribute('data-url', res.data.url);
|
||||
}).catch(function () { nameEl.textContent = '未选择文件'; showMsg('上传失败,请重试'); });
|
||||
});
|
||||
|
||||
$('btnCreate').addEventListener('click', function () {
|
||||
var form = {
|
||||
tenant_name: $('cName').value.trim(),
|
||||
credit_code: $('cCredit').value.trim(),
|
||||
legal_person: $('cLegal').value.trim(),
|
||||
license_file: $('licenseName').getAttribute('data-url') || '',
|
||||
contact_person: $('cContact').value.trim(),
|
||||
contact_phone: $('cPhone').value.trim(),
|
||||
contact_email: $('cEmail').value.trim(),
|
||||
address: $('cAddress').value.trim()
|
||||
};
|
||||
if (!form.tenant_name) { showMsg('请填写公司名称'); return; }
|
||||
if (!/^[0-9A-Z]{18}$/.test(form.credit_code.toUpperCase())) { showMsg('请填写 18 位统一社会信用代码'); return; }
|
||||
form.credit_code = form.credit_code.toUpperCase();
|
||||
if (!form.legal_person) { showMsg('请填写法定代表人姓名'); return; }
|
||||
if (!form.license_file) { showMsg('请上传营业执照电子档'); return; }
|
||||
if (!form.contact_person) { showMsg('请填写联系人'); return; }
|
||||
if (!/^1[3-9]\d{9}$/.test(form.contact_phone)) { showMsg('请填写正确的联系电话'); return; }
|
||||
if (!form.contact_email || form.contact_email.indexOf('@') < 0) { showMsg('请填写正确的电子邮箱'); return; }
|
||||
if (!form.address) { showMsg('请填写公司地址'); return; }
|
||||
|
||||
if (!confirm('提交后,当前注册账号将成为「' + form.tenant_name + '」的管理员账号,该绑定不可更改。确定提交并等待平台审核吗?')) return;
|
||||
|
||||
var btn = this; btn.disabled = true;
|
||||
post('/auth/register/apply-create', form, function (res) {
|
||||
btn.disabled = false;
|
||||
if (res.code !== 200) { showMsg(res.msg || '提交失败'); return; }
|
||||
done('申请已提交', '创建企业的申请已提交,请等待平台审核;审核通过后当前账号将成为企业管理员账号。', res.redirect || buildLoginURL(''));
|
||||
});
|
||||
});
|
||||
|
||||
// ---------- 暂不加入 ----------
|
||||
$('btnSkip').addEventListener('click', function () {
|
||||
if (!confirm('暂不加入企业将直接完成注册,之后可在登录后申请加入。确定吗?')) return;
|
||||
post('/auth/register/finish', { token: state.token }, function (res) {
|
||||
if (res.code !== 200) { showMsg(res.msg || '操作失败'); return; }
|
||||
done('注册成功', '账号已创建。你当前未加入任何企业,可稍后申请加入。', res.redirect || buildLoginURL(''));
|
||||
});
|
||||
});
|
||||
|
||||
// 回登录页时透传 client_id/redirect
|
||||
(function keepOAuthParams() {
|
||||
if (!CLIENT_ID && !REDIRECT) return;
|
||||
var suffix = (CLIENT_ID ? 'client_id=' + encodeURIComponent(CLIENT_ID) : '') +
|
||||
(REDIRECT ? (CLIENT_ID ? '&' : '') + 'redirect=' + encodeURIComponent(REDIRECT) : '');
|
||||
['toLogin', 'toForgot'].forEach(function (id) {
|
||||
var a = $(id);
|
||||
if (a) a.href = a.getAttribute('href') + (a.getAttribute('href').indexOf('?') >= 0 ? '&' : '?') + suffix;
|
||||
});
|
||||
})();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user