300 lines
17 KiB
PHP
300 lines
17 KiB
PHP
<?php
|
||
require __DIR__ . '/../includes/init.php';
|
||
requireStaff();
|
||
require '_common.php';
|
||
$isAdmin = isAdminStaff();
|
||
$admin = currentAdmin();
|
||
$msg = '';
|
||
$err = '';
|
||
// 客服账号仅可查看,禁止任何写操作(直接丢弃写请求键)
|
||
if (!$isAdmin && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||
$err = '客服账号仅可查看,如需操作请联系管理员。';
|
||
foreach (['savesite','changepw','savesmtp','saveabout','savepoints','savepay_ali','savepay_wx','savefavicon','delfavicon'] as $k) {
|
||
unset($_POST[$k]);
|
||
}
|
||
}
|
||
if (isset($_POST['savesite'])) {
|
||
verifyCsrf();
|
||
$siteName = trim($_POST['site_name'] ?? '');
|
||
if ($siteName === '') {
|
||
$err = '站点名称不能为空。';
|
||
} else {
|
||
saveSetting('site_name', $siteName);
|
||
$GLOBALS['_site_name'] = null; // 清空全局缓存
|
||
$msg = '站点名称已修改。';
|
||
}
|
||
}
|
||
if (isset($_POST['changepw'])) {
|
||
verifyCsrf();
|
||
$cur = $_POST['cur'] ?? '';
|
||
$new = $_POST['new'] ?? '';
|
||
$new2 = $_POST['new2'] ?? '';
|
||
$stmt = db()->prepare('SELECT password FROM ' . tn('users') . ' WHERE id = ?');
|
||
$stmt->execute([$admin['id']]);
|
||
$hash = $stmt->fetchColumn();
|
||
if (!password_verify($cur, $hash)) {
|
||
$err = '当前密码错误。';
|
||
} elseif (strlen($new) < 6) {
|
||
$err = '新密码至少 6 位。';
|
||
} elseif ($new !== $new2) {
|
||
$err = '两次输入的新密码不一致。';
|
||
} else {
|
||
$newHash = password_hash($new, PASSWORD_DEFAULT);
|
||
db()->prepare('UPDATE ' . tn('users') . ' SET password = ? WHERE id = ?')
|
||
->execute([$newHash, $admin['id']]);
|
||
$msg = '管理员密码已修改。';
|
||
}
|
||
}
|
||
if (isset($_POST['savesmtp'])) {
|
||
verifyCsrf();
|
||
foreach (['smtp_host', 'smtp_port', 'smtp_enc', 'smtp_user', 'smtp_pass', 'smtp_from', 'smtp_fromname', 'notify_emails'] as $k) {
|
||
saveSetting($k, trim((string) ($_POST[$k] ?? '')));
|
||
}
|
||
$msg = 'SMTP 配置已保存!';
|
||
}
|
||
if (isset($_POST['saveabout'])) {
|
||
verifyCsrf();
|
||
saveSetting('mall_about', trim((string) ($_POST['mall_about'] ?? '')));
|
||
$msg = '商城介绍已保存!';
|
||
}
|
||
if (isset($_POST['savepoints'])) {
|
||
verifyCsrf();
|
||
saveSetting('points_enabled', isset($_POST['points_enabled']) ? '1' : '0');
|
||
saveSetting('points_sign', max(0, (int) ($_POST['points_sign'] ?? 0)));
|
||
saveSetting('points_invite', max(0, (int) ($_POST['points_invite'] ?? 0)));
|
||
$msg = '积分设置已保存';
|
||
}
|
||
if (isset($_POST['savepay_ali'])) {
|
||
verifyCsrf();
|
||
saveSetting('pay_alipay_enabled', isset($_POST['pay_alipay_enabled']) ? '1' : '0');
|
||
saveSetting('pay_alipay_appid', trim((string) ($_POST['pay_alipay_appid'] ?? '')));
|
||
saveSetting('pay_alipay_private_key', trim((string) ($_POST['pay_alipay_private_key'] ?? '')));
|
||
saveSetting('pay_alipay_public_key', trim((string) ($_POST['pay_alipay_public_key'] ?? '')));
|
||
saveSetting('pay_alipay_sandbox', isset($_POST['pay_alipay_sandbox']) ? '1' : '0');
|
||
$msg = '支付宝配置已保存!';
|
||
}
|
||
if (isset($_POST['savepay_wx'])) {
|
||
verifyCsrf();
|
||
saveSetting('pay_wechat_enabled', isset($_POST['pay_wechat_enabled']) ? '1' : '0');
|
||
saveSetting('pay_wechat_mch_id', trim((string) ($_POST['pay_wechat_mch_id'] ?? '')));
|
||
saveSetting('pay_wechat_api_key', trim((string) ($_POST['pay_wechat_api_key'] ?? '')));
|
||
saveSetting('pay_wechat_appid', trim((string) ($_POST['pay_wechat_appid'] ?? '')));
|
||
$msg = '微信支付配置已保存!';
|
||
}
|
||
if (isset($_POST['savefavicon']) || isset($_POST['delfavicon'])) {
|
||
verifyCsrf();
|
||
if (!$isAdmin) {
|
||
$err = '客服账号仅可查看,如需操作请联系管理员。';
|
||
} elseif (isset($_POST['delfavicon'])) {
|
||
$old = getSetting('favicon', '');
|
||
if ($old && file_exists(__DIR__ . '/../' . $old)) { @unlink(__DIR__ . '/../' . $old); }
|
||
saveSetting('favicon', '');
|
||
$msg = '已恢复默认图标。';
|
||
} else {
|
||
if (empty($_FILES['favicon_file']) || $_FILES['favicon_file']['error'] !== UPLOAD_ERR_OK) {
|
||
$err = '请选择要上传的 .ico 文件。';
|
||
} else {
|
||
$f = $_FILES['favicon_file'];
|
||
$ext = strtolower(pathinfo($f['name'], PATHINFO_EXTENSION));
|
||
if ($ext !== 'ico') {
|
||
$err = '仅支持 .ico 格式的图标文件。';
|
||
} elseif ($f['size'] > 256 * 1024) {
|
||
$err = '文件过大,请控制在 256KB 以内。';
|
||
} else {
|
||
// 校验 ICO 文件头:00 00 01 00
|
||
$head = file_get_contents($f['tmp_name'], false, null, 0, 4);
|
||
if ($head !== "\x00\x00\x01\x00") {
|
||
$err = '文件内容不是有效的 ICO 图标(文件头校验失败)。';
|
||
} else {
|
||
$dest = __DIR__ . '/../assets/favicon.ico';
|
||
if (!move_uploaded_file($f['tmp_name'], $dest) && !copy($f['tmp_name'], $dest)) {
|
||
$err = '保存失败,请检查 assets/ 目录写入权限。';
|
||
} else {
|
||
saveSetting('favicon', 'assets/favicon.ico');
|
||
$msg = '网站图标已更新,刷新浏览器即可看到(若未变请强制刷新清缓存)。';
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
$testResult = null;
|
||
if (isset($_POST['testmail'])) {
|
||
verifyCsrf();
|
||
$to = trim($_POST['test_to'] ?? '');
|
||
if (!filter_var($to, FILTER_VALIDATE_EMAIL)) {
|
||
$err = '请填写有效的测试收件邮箱。';
|
||
} else {
|
||
$cfg = smtpConfig();
|
||
$body = '<div style="font-family:sans-serif;max-width:520px;margin:auto">'
|
||
. '<h2 style="color:#2563eb">[' . h(SITE_NAME) . '] SMTP 测试邮件</h2>'
|
||
. '<p>这是一封由商城后台发送的测试邮件,若你收到了它,说明 SMTP 配置正确。</p>'
|
||
. '<p class="muted">发送时间:' . date('Y-m-d H:i:s') . '</p></div>';
|
||
$testResult = fnwSendMail($to, '[' . SITE_NAME . '] SMTP 测试邮件', $body);
|
||
if ($testResult['ok']) {
|
||
$msg = '测试邮件已发送,请查收(含 SMTP 会话日志见下方)。';
|
||
} else {
|
||
$err = '发送失败:' . ($testResult['error'] ?? '未知错误');
|
||
}
|
||
}
|
||
}
|
||
$cfg = smtpConfig();
|
||
adminHeader('设置', 'settings');
|
||
?>
|
||
<h1 class="page-title"><i class="fas fa-cog"></i> 设置</h1>
|
||
<?php if ($msg): ?><p class="banner-ok"><i class="fas fa-check-circle"></i> <?= h($msg) ?></p><?php endif; ?>
|
||
<?php if ($err): ?><p class="form-err"><?= h($err) ?></p><?php endif; ?>
|
||
<div class="panel" style="max-width:560px;">
|
||
<h3>修改管理员密码</h3>
|
||
<p class="muted">当前管理员:<strong><?= h($admin['username']) ?></strong></p>
|
||
<form method="post" action="" class="auth-form">
|
||
<?= csrfField() ?>
|
||
<label>当前密码<input type="password" name="cur" required></label>
|
||
<label>新密码<input type="password" name="new" placeholder="至少 6 位。" required></label>
|
||
<label>确认新密码<input type="password" name="new2" required></label>
|
||
<button type="submit" name="changepw" class="btn btn-primary">保存修改</button>
|
||
</form>
|
||
</div>
|
||
|
||
<div class="panel" style="max-width:680px;">
|
||
<h3>商城介绍(前台首页展示)</h3>
|
||
<p class="muted">这段文字会显示在商城首页「关于商城」区块,向访客介绍你的商城。留空则不显示该区块。</p>
|
||
<form method="post" action="" class="grid-form">
|
||
<?= csrfField() ?>
|
||
<label class="span2">商城介绍<textarea name="mall_about" rows="4" placeholder="如:本商城是 XX 团队旗下的……"><?= h(getSetting('mall_about', '')) ?></textarea></label>
|
||
<div class="form-actions span2">
|
||
<button type="submit" name="saveabout" class="btn btn-primary"><i class="fas fa-save"></i> 保存商城介绍</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
<div class="panel" style="max-width:560px;">
|
||
<h3><i class="fas fa-store"></i> 站点名称</h3>
|
||
<p class="muted">该名称会显示在浏览器标题、页面顶部、邮件通知等位置。</p>
|
||
<form method="post" action="" class="auth-form">
|
||
<?= csrfField() ?>
|
||
<label>站点名称
|
||
<input type="text" name="site_name" value="<?= h(getSetting('site_name', SITE_NAME)) ?>" required>
|
||
</label>
|
||
<button type="submit" name="savesite" class="btn btn-primary"><i class="fas fa-save"></i> 保存站点名称</button>
|
||
</form>
|
||
</div>
|
||
<div class="panel" style="max-width:560px;">
|
||
<h3><i class="fas fa-image"></i> 网站图标(Favicon)</h3>
|
||
<p class="muted">上传 .ico 格式图标(建议 16×16 或 32×32,最大 256KB),将显示在浏览器标签页。留空则使用默认图标。</p>
|
||
<p style="margin:12px 0;">
|
||
<?php
|
||
$favCur = getSetting('favicon', '');
|
||
if ($favCur && file_exists(__DIR__ . '/../' . $favCur)):
|
||
?>
|
||
<img src="../<?= h($favCur) ?>?v=<?= filemtime(__DIR__ . '/../' . $favCur) ?>" alt="当前图标" style="width:32px;height:32px;border:1px solid var(--border);border-radius:6px;vertical-align:middle;">
|
||
<span class="muted"> 当前图标(<?= h($favCur) ?>)</span>
|
||
<?php else: ?>
|
||
<span class="muted">当前:默认图标</span>
|
||
<?php endif; ?>
|
||
</p>
|
||
<form method="post" action="" enctype="multipart/form-data" class="auth-form">
|
||
<?= csrfField() ?>
|
||
<label>选择 .ico 文件
|
||
<input type="file" name="favicon_file" accept=".ico,image/vnd.microsoft.icon">
|
||
</label>
|
||
<div class="form-actions">
|
||
<button type="submit" name="savefavicon" class="btn btn-primary"><i class="fas fa-upload"></i> 上传并启用</button>
|
||
</div>
|
||
</form>
|
||
<?php if ($favCur): ?>
|
||
<form method="post" action="" class="auth-form" style="margin-top:8px;">
|
||
<?= csrfField() ?>
|
||
<button type="submit" name="delfavicon" class="btn btn-ghost" onclick="return confirm('确定恢复为默认图标?');"><i class="fas fa-undo"></i> 恢复默认</button>
|
||
</form>
|
||
<?php endif; ?>
|
||
</div>
|
||
<div class="panel" style="max-width:680px;">
|
||
<h3>积分系统设置</h3>
|
||
<p class="muted">开启后,用户可在前台「每日签到」获取积分、通过邀请链接邀请好友获得积分;商品可设置「积分价」,结算时选择「积分支付」。</p>
|
||
<form method="post" action="" class="grid-form">
|
||
<?= csrfField() ?>
|
||
<label class="checkbox"><input type="checkbox" name="points_enabled" <?= getSetting('points_enabled', '1')==='1' ? 'checked' : '' ?>> 启用积分系统</label>
|
||
<label>每日签到可得积分<input type="number" min="0" name="points_sign" value="<?= h(getSetting('points_sign', '5')) ?>" placeholder="如 5"></label>
|
||
<label>邀请好友成功可得积分<input type="number" min="0" name="points_invite" value="<?= h(getSetting('points_invite', '20')) ?>" placeholder="如 20"></label>
|
||
<div class="form-actions span2">
|
||
<button type="submit" name="savepoints" class="btn btn-primary"><i class="fas fa-save"></i> 保存积分设置</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<div class="panel" style="max-width:680px;">
|
||
<h3>SMTP 邮件配置</h3>
|
||
<p class="muted">用于:用户提交工单后通知处理人员、处理回复通知用户。常见配置:QQ 邮箱 <code>smtp.qq.com</code> / 端口 <code>465</code> / 加密 <code>SSL</code> / 密码填「授权码」。</p>
|
||
<form method="post" action="" class="grid-form">
|
||
<?= csrfField() ?>
|
||
<label>SMTP 主机<input type="text" name="smtp_host" value="<?= h($cfg['smtp_host']) ?>" placeholder="如 smtp.qq.com"></label>
|
||
<label>端口<input type="text" name="smtp_port" value="<?= h($cfg['smtp_port']) ?>" placeholder="如 465"></label>
|
||
<label>加密方式
|
||
<select name="smtp_enc">
|
||
<option value="" <?= $cfg['smtp_enc']==='' ?'selected':'' ?>>无</option>
|
||
<option value="ssl" <?= $cfg['smtp_enc']==='ssl' ?'selected':'' ?>>SSL(465)</option>
|
||
<option value="tls" <?= $cfg['smtp_enc']==='tls' ?'selected':'' ?>>STARTTLS(587)</option>
|
||
</select>
|
||
</label>
|
||
<label>发件人名称<input type="text" name="smtp_fromname" value="<?= h($cfg['smtp_fromname']) ?>" placeholder="如 自由云商城"></label>
|
||
<label class="span2">登录账号(邮箱)<input type="text" name="smtp_user" value="<?= h($cfg['smtp_user']) ?>" placeholder="SMTP 登录邮箱"></label>
|
||
<label class="span2">登录密码 / 授权码<input type="password" name="smtp_pass" value="<?= h($cfg['smtp_pass']) ?>" placeholder="QQ 邮箱填授权码"></label>
|
||
<label class="span2">发件人邮箱<input type="text" name="smtp_from" value="<?= h($cfg['smtp_from']) ?>" placeholder="留空则同登录账号"></label>
|
||
<label class="span2">通知收件人(处理人员,逗号分隔)<input type="text" name="notify_emails" value="<?= h($cfg['notify_emails']) ?>" placeholder="如 admin@example.com,ops@example.com"></label>
|
||
<div class="form-actions span2">
|
||
<button type="submit" name="savesmtp" class="btn btn-primary"><i class="fas fa-save"></i> 保存 SMTP 配置</button>
|
||
</div>
|
||
</form>
|
||
|
||
<hr style="margin:22px 0;border:none;border-top:1px solid var(--border);">
|
||
<h3>发送测试邮件</h3>
|
||
<form method="post" action="" class="grid-form" style="max-width:480px;">
|
||
<?= csrfField() ?>
|
||
<label class="span2">测试收件邮箱<input type="email" name="test_to" placeholder="输入一个能收信的邮箱" required></label>
|
||
<div class="form-actions span2">
|
||
<button type="submit" name="testmail" class="btn btn-ghost"><i class="fas fa-paper-plane"></i> 发送测试邮件</button>
|
||
</div>
|
||
</form>
|
||
|
||
<?php if ($testResult): ?>
|
||
<div class="smtp-log">
|
||
<div class="muted" style="margin-bottom:6px;">SMTP 会话日志:</div>
|
||
<pre><?= h(implode("\n", $testResult['log'])) ?></pre>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<div class="panel" style="max-width:780px;">
|
||
<h3><i class="fab fa-alipay"></i> 支付接口配置</h3>
|
||
<p class="muted">配置后用户下单可选择对应支付方式。未启用(或未填密钥)的渠道不会在前台显示。沙箱模式仅支付宝支持,仅用于联调,正式上线请关闭。</p>
|
||
|
||
<h4 style="margin:18px 0 10px;">支付宝(电脑网站支付 · RSA2)</h4>
|
||
<form method="post" action="" class="grid-form">
|
||
<?= csrfField() ?>
|
||
<label class="checkbox"><input type="checkbox" name="pay_alipay_enabled" <?= getSetting('pay_alipay_enabled','0')==='1'?'checked':'' ?>> 启用支付宝支付</label>
|
||
<label class="checkbox"><input type="checkbox" name="pay_alipay_sandbox" <?= getSetting('pay_alipay_sandbox','0')==='1'?'checked':'' ?>> 沙箱模式(调试用)</label>
|
||
<label class="span2">应用 APPID<input type="text" name="pay_alipay_appid" value="<?= h(getSetting('pay_alipay_appid','')) ?>" placeholder="如 2021000000000000"></label>
|
||
<label class="span2">应用私钥(RSA2)<textarea name="pay_alipay_private_key" rows="3" placeholder="-----BEGIN PRIVATE KEY----- ..."><?= h(getSetting('pay_alipay_private_key','')) ?></textarea></label>
|
||
<label class="span2">支付宝公钥<textarea name="pay_alipay_public_key" rows="3" placeholder="-----BEGIN PUBLIC KEY----- ..."><?= h(getSetting('pay_alipay_public_key','')) ?></textarea></label>
|
||
<div class="form-actions span2">
|
||
<button type="submit" name="savepay_ali" class="btn btn-primary"><i class="fas fa-save"></i> 保存支付宝配置</button>
|
||
</div>
|
||
</form>
|
||
|
||
<hr style="margin:24px 0;border:none;border-top:1px solid var(--border);">
|
||
|
||
<h4 style="margin:18px 0 10px;">微信支付(Native 扫码支付 · V3)</h4>
|
||
<form method="post" action="" class="grid-form">
|
||
<?= csrfField() ?>
|
||
<label class="checkbox"><input type="checkbox" name="pay_wechat_enabled" <?= getSetting('pay_wechat_enabled','0')==='1'?'checked':'' ?>> 启用微信支付</label>
|
||
<label class="span2">商户号 MCH ID<input type="text" name="pay_wechat_mch_id" value="<?= h(getSetting('pay_wechat_mch_id','')) ?>" placeholder="如 1900000000"></label>
|
||
<label class="span2">API 密钥(Key / APIv3 Key)<input type="text" name="pay_wechat_api_key" value="<?= h(getSetting('pay_wechat_api_key','')) ?>" placeholder="32 位密钥"></label>
|
||
<label class="span2">APPID(公众号 / 小程序,可选)<input type="text" name="pay_wechat_appid" value="<?= h(getSetting('pay_wechat_appid','')) ?>" placeholder="留空则用商户默认"></label>
|
||
<div class="form-actions span2">
|
||
<button type="submit" name="savepay_wx" class="btn btn-primary"><i class="fas fa-save"></i> 保存微信支付配置</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<?php adminFooter(); ?>
|