Files
2026-08-14 10:55:06 +08:00

167 lines
7.8 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// 用户设置:修改用户名 / 邮箱 / 昵称
require __DIR__ . '/includes/init.php';
requireLogin('settings.php');
$pageTitle = __('settings_page.title');
$stmt = db()->prepare('SELECT id, username, email, nickname, verified, is_admin, status, avatar FROM ' . tn('users') . ' WHERE id = ?');
$stmt->execute([$_SESSION['user_id']]);
$user = $stmt->fetch();
$msg = '';
$msgOk = false;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
verifyCsrf();
$u = trim($_POST['username'] ?? '');
$em = trim($_POST['email'] ?? '');
$nk = trim($_POST['nickname'] ?? '');
if (mb_strlen($u) < 3) $msg = __('settings_page.err_username_short');
elseif (!filter_var($em, FILTER_VALIDATE_EMAIL)) $msg = __('settings_page.err_email_invalid');
elseif ($nk !== '' && mb_strlen($nk) > 30) $msg = __('settings_page.err_nickname_long');
if ($msg === '') {
$chk = db()->prepare('SELECT id FROM ' . tn('users') . ' WHERE username = ? AND id <> ?');
$chk->execute([$u, $user['id']]);
if ($chk->fetch()) {
$msg = __('settings_page.err_username_taken');
} else {
$emailChanged = ($em !== $user['email']);
db()->prepare('UPDATE ' . tn('users') . ' SET username = ?, email = ?, nickname = ? WHERE id = ?')
->execute([$u, $em, $nk, $user['id']]);
// 更换邮箱则取消验证状态,要求重新验证
if ($emailChanged && !empty($user['verified'])) {
db()->prepare('UPDATE ' . tn('users') . ' SET verified = 0 WHERE id = ?')
->execute([$user['id']]);
}
// 重新读取最新资料
$stmt->execute([$user['id']]);
$user = $stmt->fetch();
$msg = $emailChanged ? __('settings_page.saved_email_change') : __('settings_page.saved');
$msgOk = true;
}
}
}
require 'includes/header.php';
?>
<section class="section">
<h2 class="sec-title"><i class="fas fa-cog"></i> <?= __('settings_page.title') ?></h2>
<?php if ($msg): ?>
<p class="<?= $msgOk ? 'banner-ok' : 'form-err' ?>"><?= h($msg) ?></p>
<?php endif; ?>
<form method="post" action="" class="grid-form settings-form">
<?= csrfField() ?>
<!-- 头像上传 -->
<div class="span2" style="margin-bottom:18px">
<h3 style="color:var(--primary);font-size:1.05rem;margin-bottom:12px"><i class="fas fa-camera"></i> <?= __('settings_page.avatar_section') ?></h3>
<div style="display:flex;align-items:center;gap:16px;flex-wrap:wrap">
<div id="avatarPreviewWrap" style="width:80px;height:80px;border-radius:50%;overflow:hidden;background:var(--primary-container);display:flex;align-items:center;justify-content:center;border:3px solid var(--outline);flex-shrink:0">
<?php if (!empty($user['avatar'])): ?>
<img src="<?= h($user['avatar']) ?>" alt="" style="width:100%;height:100%;object-fit:cover">
<?php else: ?>
<span style="font-size:1.8rem;font-weight:700;color:var(--on-primary-container)"><?= h(avatarText($user['nickname'] ?: $user['username'])) ?></span>
<?php endif; ?>
</div>
<div>
<label for="avatarInput" class="btn btn-sm btn-ghost" style="cursor:pointer;display:inline-flex;align-items:center;gap:6px">
<i class="fas fa-upload"></i> <?= __('settings_page.select_image') ?>
</label>
<input type="file" id="avatarInput" accept="image/jpeg,image/png,image/webp,image/gif" style="display:none">
<p style="font-size:.8rem;color:var(--muted);margin-top:4px"><?= __('settings_page.avatar_hint') ?></p>
</div>
</div>
<p id="avatarMsg" style="font-size:.88rem;margin-top:6px"></p>
</div>
<label><?= __('auth.username') ?><input type="text" name="username" value="<?= h($user['username']) ?>" required></label>
<label><?= __('auth.email') ?><input type="email" name="email" value="<?= h($user['email']) ?>" required></label>
<label class="span2"><?= __('user.nickname') ?><input type="text" name="nickname" value="<?= h($user['nickname']) ?>" placeholder="<?= __('settings_page.nickname_placeholder') ?>" maxlength="30"></label>
<div class="form-actions span2">
<button type="submit" class="btn btn-primary"><?= __('settings_page.save_btn') ?></button>
<a href="myorders.php" class="btn btn-ghost"><?= __('settings_page.back_orders') ?></a>
</div>
</form>
<div class="panel verify-panel">
<h3><i class="fas fa-envelope-circle-check"></i> <?= __('settings_page.verify_section') ?></h3>
<?php if (!empty($user['verified'])): ?>
<p class="ok-line"><i class="fas fa-circle-check"></i> <?= __('settings_page.verify_done') ?></p>
<?php else: ?>
<p class="warn-line"><i class="fas fa-triangle-exclamation"></i> <?= str_replace('{link}', '<a href="verify.php?resend=1">' . __('settings_page.verify_send_link') . '</a>', __('settings_page.verify_not_done')) ?></p>
<?php endif; ?>
</div>
</section>
<script>
// 头像上传(AJAX
(function () {
var input = document.getElementById('avatarInput');
var msg = document.getElementById('avatarMsg');
var wrap = document.getElementById('avatarPreviewWrap');
if (!input) return;
input.addEventListener('change', function () {
if (!input.files || !input.files[0]) return;
var file = input.files[0];
// 前端预校验
if (file.size > 2 * 1024 * 1024) {
msg.textContent = '<?= __('settings_page.img_too_big') ?>';
msg.style.color = 'var(--danger)';
return;
}
// 即时预览
var reader = new FileReader();
reader.onload = function (e) {
wrap.innerHTML = '<img src="' + e.target.result + '" alt="" style="width:100%;height:100%;object-fit:cover;border-radius:50%">';
};
reader.readAsDataURL(file);
// 上传
msg.textContent = '<?= __('settings_page.uploading') ?>';
msg.style.color = 'var(--muted)';
var fd = new FormData();
fd.append('avatar', file);
fd.append('csrf_token', Fnw.csrf());
var xhr = new XMLHttpRequest();
xhr.open('POST', 'avatar_upload.php', true);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.onload = function () {
if (xhr.status === 200) {
try { var res = JSON.parse(xhr.responseText); } catch (e) { res = { ok: false, msg: '<?= __('settings_page.response_err') ?>' }; }
if (res.ok) {
msg.textContent = res.msg || '<?= __('settings_page.upload_ok') ?>';
msg.style.color = 'var(--ok)';
updateAvatars(res.data.url);
} else {
msg.textContent = res.msg || '<?= __('settings_page.upload_fail') ?>';
msg.style.color = 'var(--danger)';
}
} else {
msg.textContent = '<?= str_replace('{code}', "' + xhr.status + '", __('settings_page.network_err')) ?>';
msg.style.color = 'var(--danger)';
}
};
xhr.onerror = function () {
msg.textContent = '<?= __('settings_page.network_retry') ?>';
msg.style.color = 'var(--danger)';
};
xhr.send(fd);
});
function updateAvatars(url) {
document.querySelectorAll('.avatar, .uc-avatar').forEach(function (el) {
el.innerHTML = '<img src="' + url + '" alt="" style="width:100%;height:100%;object-fit:cover;border-radius:50%">';
});
}
})();
</script>
<?php require 'includes/footer.php'; ?>