Files
YwxAppThink/public/install/index.html
T

205 lines
10 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>系统安装向导</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: system-ui, -apple-system, "Microsoft YaHei", sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 20px; }
.wrap { width: 100%; max-width: 620px; background: #fff; border-radius: 14px; box-shadow: 0 15px 40px rgba(0,0,0,.2); overflow: hidden; }
.hd { background: linear-gradient(135deg, #667eea, #764ba2); color: #fff; padding: 26px 30px; }
.hd h1 { font-size: 22px; font-weight: 600; }
.hd p { margin-top: 6px; opacity: .85; font-size: 13px; }
.steps { display: flex; padding: 16px 30px 0; gap: 8px; }
.step { flex: 1; text-align: center; font-size: 12px; color: #aaa; padding-bottom: 10px; border-bottom: 2px solid #eee; }
.step.active { color: #667eea; border-color: #667eea; font-weight: 600; }
.step.done { color: #2ecc71; border-color: #2ecc71; }
.bd { padding: 24px 30px 30px; }
.panel { display: none; }
.panel.show { display: block; }
.env-item { display: flex; justify-content: space-between; padding: 9px 0; border-bottom: 1px dashed #eee; font-size: 14px; }
.env-item .st { font-weight: 600; }
.ok { color: #2ecc71; } .bad { color: #e74c3c; }
label { display: block; font-size: 13px; color: #555; margin: 14px 0 6px; }
input { width: 100%; padding: 10px 12px; border: 1px solid #ddd; border-radius: 8px; font-size: 14px; outline: none; transition: border .2s; }
input:focus { border-color: #667eea; }
.row { display: flex; gap: 12px; }
.row > div { flex: 1; }
.btn { display: inline-block; margin-top: 22px; padding: 11px 26px; background: linear-gradient(135deg, #667eea, #764ba2); color: #fff; border: none; border-radius: 8px; font-size: 15px; cursor: pointer; transition: opacity .2s; }
.btn:hover { opacity: .9; }
.btn:disabled { background: #ccc; cursor: not-allowed; }
.msg { margin-top: 16px; padding: 12px 14px; border-radius: 8px; font-size: 14px; display: none; }
.msg.info { background: #eef4ff; color: #3b6fd4; display: block; }
.msg.err { background: #fdecea; color: #e74c3c; display: block; }
.msg.succ { background: #eafaf1; color: #27ae60; display: block; }
.hint { font-size: 12px; color: #999; margin-top: 4px; }
code { background: #f4f4f6; padding: 1px 5px; border-radius: 4px; font-size: 12px; }
</style>
</head>
<body>
<div class="wrap">
<div class="hd">
<h1>系统安装向导</h1>
<p>请按步骤完成数据库配置与初始化</p>
</div>
<div class="steps">
<div class="step active" id="s1">1. 环境检测</div>
<div class="step" id="s2">2. 数据库配置</div>
<div class="step" id="s3">3. 执行安装</div>
<div class="step" id="s4">4. 完成</div>
</div>
<div class="bd">
<!-- 步骤1 -->
<div class="panel show" id="p1">
<div id="envList"><div class="hint">正在检测运行环境…</div></div>
<button class="btn" id="toStep2" disabled>下一步</button>
</div>
<!-- 步骤2 -->
<div class="panel" id="p2">
<label>数据库主机</label>
<input name="db_host" value="127.0.0.1">
<div class="row">
<div>
<label>端口</label>
<input name="db_port" value="3306">
</div>
<div>
<label>字符集</label>
<input name="db_charset" value="utf8mb4">
</div>
</div>
<label>数据库名</label>
<input name="db_name" placeholder="如 ywxapp">
<label>数据库用户名</label>
<input name="db_user" placeholder="如 root">
<label>数据库密码</label>
<input name="db_pass" type="password" placeholder="建议不含 # = ; 等特殊字符">
<label>表前缀</label>
<input name="db_prefix" value="wxapp_">
<div class="hint">若数据库不存在,向导将自动创建;密码请避免 <code>#</code> <code>=</code> <code>;</code> 等特殊字符。</div>
<div style="margin-top:18px;padding-top:14px;border-top:1px dashed #eee">
<div style="font-size:13px;color:#667eea;font-weight:600;margin-bottom:4px">管理员账号</div>
<div class="hint" style="margin-bottom:8px">该账号用于登录系统后台,请牢记。</div>
<label>管理员账号</label>
<input name="admin_account" value="admin" placeholder="如 admin">
<label>管理员密码</label>
<input name="admin_password" type="password" placeholder="设置后台登录密码">
<label>管理员昵称</label>
<input name="admin_nickname" value="超级管理员" placeholder="如 超级管理员">
<div class="row">
<div>
<label>管理员邮箱</label>
<input name="admin_email" placeholder="可选">
</div>
<div>
<label>管理员手机</label>
<input name="admin_mobile" placeholder="可选">
</div>
</div>
</div>
<button class="btn" id="testDb">测试连接</button>
<button class="btn" id="toStep3" style="background:#2ecc71">开始安装</button>
<div class="msg" id="dbMsg"></div>
</div>
<!-- 步骤3 -->
<div class="panel" id="p3">
<div class="msg info" id="installMsg">正在导入数据库结构与初始数据,请稍候…</div>
</div>
<!-- 步骤4 -->
<div class="panel" id="p4">
<div class="msg succ">🎉 安装完成!</div>
<p style="margin-top:14px;font-size:14px;color:#555;line-height:1.7">
数据库已初始化,配置文件 <code>.env</code> 已写入,安装锁 <code>install.lock</code> 已生成。<br>
现在可以访问前台/后台开始使用。<br><br>
<b>安全提示:</b>生产环境部署完成后,建议删除 <code>public/install/</code> 目录与 <code>public/tools/</code> 目录,避免被重复安装。
</p>
</div>
</div>
</div>
<script>
function setStep(n) {
for (var i = 1; i <= 4; i++) {
document.getElementById('p' + i).classList.toggle('show', i === n);
var s = document.getElementById('s' + i);
s.classList.toggle('active', i === n);
s.classList.toggle('done', i < n);
}
}
function form2obj() {
var o = {}, els = document.querySelectorAll('#p2 input');
els.forEach(function (e) { o[e.name] = e.value; });
return o;
}
function post(action, data) {
return fetch('index.php?action=' + action, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: Object.keys(data).map(function (k) { return k + '=' + encodeURIComponent(data[k]); }).join('&')
}).then(function (r) { return r.json(); });
}
// 步骤1:环境检测
fetch('index.php?action=env').then(function (r) { return r.json(); }).then(function (res) {
var box = document.getElementById('envList');
if (!res.ok) { box.innerHTML = '<div class="hint">检测失败</div>'; return; }
var allOk = true, html = '';
res.items.forEach(function (it) {
if (!it.ok) allOk = false;
html += '<div class="env-item"><span>' + it.name + ' <span class="hint">(' + it.require + ')</span></span>'
+ '<span class="st ' + (it.ok ? 'ok' : 'bad') + '">' + it.current + (it.ok ? ' ✓' : ' ✗') + '</span></div>';
});
box.innerHTML = html;
document.getElementById('toStep2').disabled = !allOk;
if (!allOk) {
var d = document.createElement('div');
d.className = 'msg err'; d.style.display = 'block';
d.textContent = '存在不满足的环境要求,请先修复后刷新页面。';
box.appendChild(d);
}
});
document.getElementById('toStep2').onclick = function () { setStep(2); };
// 步骤2:测试连接
document.getElementById('testDb').onclick = function () {
var m = document.getElementById('dbMsg');
m.className = 'msg info'; m.textContent = '正在测试连接…';
post('dbtest', form2obj()).then(function (r) {
if (r.ok) { m.className = 'msg succ'; m.textContent = '连接成功,数据库已就绪。'; }
else { m.className = 'msg err'; m.textContent = '连接失败:' + (r.msg || '未知错误'); }
}).catch(function () { m.className = 'msg err'; m.textContent = '请求异常'; });
};
// 步骤2 -> 3:开始安装
document.getElementById('toStep3').onclick = function () {
var data = form2obj();
if (!data.db_name || !data.db_user) {
var m = document.getElementById('dbMsg');
m.className = 'msg err'; m.textContent = '请填写数据库名与用户名。';
return;
}
if (!data.admin_account || !data.admin_password) {
var m2 = document.getElementById('dbMsg');
m2.className = 'msg err'; m2.textContent = '请填写管理员账号与密码。';
return;
}
setStep(3);
post('install', data).then(function (r) {
var im = document.getElementById('installMsg');
if (r.ok) {
im.className = 'msg succ'; im.textContent = '数据库初始化完成,配置已写入。';
setStep(4);
} else {
im.className = 'msg err'; im.textContent = (r.msg || '安装失败');
}
}).catch(function () {
document.getElementById('installMsg').className = 'msg err';
document.getElementById('installMsg').textContent = '请求异常,请检查服务器日志。';
});
};
</script>
</body>
</html>