189 lines
8.5 KiB
HTML
189 lines
8.5 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block content %}
|
|
<div class="container py-4">
|
|
<div class="row justify-content-center">
|
|
<div class="col-md-8">
|
|
<!-- 個人資料卡片 -->
|
|
<div class="card shadow-sm mb-4">
|
|
<div class="card-header bg-primary text-white">
|
|
<h5 class="card-title mb-0">個人資料</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<!-- 頭像上傳表單 -->
|
|
<form id="avatarForm" method="POST" action="{{ url_for('auth.profile') }}" enctype="multipart/form-data" class="mb-4">
|
|
<input type="hidden" name="action" value="update_avatar">
|
|
<div class="row">
|
|
<div class="col-auto">
|
|
<div class="position-relative">
|
|
{% if current_user.avatar_path %}
|
|
<img src="{{ url_for('static', filename=current_user.avatar_path) }}"
|
|
class="rounded-circle"
|
|
style="width: 100px; height: 100px; object-fit: cover;"
|
|
alt="用戶頭像">
|
|
{% else %}
|
|
<div class="avatar-circle bg-primary text-white d-flex align-items-center justify-content-center"
|
|
style="width: 100px; height: 100px; border-radius: 50%; font-size: 2.5rem;">
|
|
{{ current_user.username[0].upper() }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="position-absolute bottom-0 end-0">
|
|
<label for="avatar"
|
|
class="btn btn-light rounded-circle d-flex align-items-center justify-content-center shadow-sm"
|
|
style="width: 36px; height: 36px; padding: 0; cursor: pointer; margin-right: -8px; margin-bottom: -8px;">
|
|
<i class="bi bi-camera" style="font-size: 1.2rem;"></i>
|
|
</label>
|
|
<input type="file" id="avatar" name="avatar"
|
|
class="d-none" accept="image/*"
|
|
onchange="handleAvatarChange(this);">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col">
|
|
<h5 class="mb-1">{{ current_user.username }}</h5>
|
|
<p class="text-muted mb-0">{{ current_user.email }}</p>
|
|
<small class="text-muted">加入時間:{{ current_user.created_at.strftime('%Y-%m-%d') }}</small>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
<!-- 個人資料表單 -->
|
|
<form method="POST" action="{{ url_for('auth.profile') }}">
|
|
<input type="hidden" name="action" value="update_profile">
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">用戶名</label>
|
|
<input type="text" class="form-control" name="username"
|
|
value="{{ current_user.username }}" required>
|
|
<div class="form-text">用戶名將顯示在您的個人檔案中</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">電子郵件</label>
|
|
<input type="email" class="form-control" name="email"
|
|
value="{{ current_user.email }}" required>
|
|
<div class="form-text">用於接收通知和重要更新</div>
|
|
</div>
|
|
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="bi bi-save"></i> 保存變更
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 密碼修改卡片 -->
|
|
<div class="card shadow-sm">
|
|
<div class="card-header bg-primary text-white">
|
|
<h5 class="card-title mb-0">修改密碼</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<form method="POST" action="{{ url_for('auth.profile') }}" onsubmit="return validatePasswordForm()">
|
|
<input type="hidden" name="action" value="update_password">
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">目前密碼</label>
|
|
<input type="password" class="form-control" name="current_password" required>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">新密碼</label>
|
|
<input type="password" class="form-control" name="new_password"
|
|
id="new_password" required>
|
|
<div class="form-text">密碼必須包含至少8個字元</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">確認新密碼</label>
|
|
<input type="password" class="form-control" name="confirm_password"
|
|
id="confirm_password" required>
|
|
</div>
|
|
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="bi bi-key"></i> 更新密碼
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<script>
|
|
function handleAvatarChange(input) {
|
|
if (input.files && input.files[0]) {
|
|
const file = input.files[0];
|
|
|
|
// 檢查文件大小
|
|
if (file.size > 1024 * 1024) {
|
|
alert('圖片大小不能超過 1MB');
|
|
input.value = '';
|
|
return;
|
|
}
|
|
|
|
// 檢查文件類型
|
|
if (!file.type.startsWith('image/')) {
|
|
alert('請上傳圖片文件');
|
|
input.value = '';
|
|
return;
|
|
}
|
|
|
|
// 預覽圖片
|
|
const reader = new FileReader();
|
|
reader.onload = function (e) {
|
|
const avatar = input.closest('.position-relative').querySelector('img');
|
|
if (avatar) {
|
|
avatar.src = e.target.result;
|
|
} else {
|
|
const avatarCircle = input.closest('.position-relative').querySelector('.avatar-circle');
|
|
const newAvatar = document.createElement('img');
|
|
newAvatar.src = e.target.result;
|
|
newAvatar.classList.add('rounded-circle');
|
|
newAvatar.style.width = '100px';
|
|
newAvatar.style.height = '100px';
|
|
newAvatar.style.objectFit = 'cover';
|
|
avatarCircle.replaceWith(newAvatar);
|
|
}
|
|
}
|
|
reader.readAsDataURL(file);
|
|
|
|
// 只提交頭像表單
|
|
document.getElementById('avatarForm').submit();
|
|
}
|
|
}
|
|
|
|
function validatePasswordForm() {
|
|
const newPassword = document.getElementById('new_password').value;
|
|
const confirmPassword = document.getElementById('confirm_password').value;
|
|
|
|
if (newPassword.length < 8) {
|
|
alert('新密碼長度必須至少8個字元');
|
|
return false;
|
|
}
|
|
|
|
if (newPassword !== confirmPassword) {
|
|
alert('兩次輸入的密碼不一致');
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Flash 消息自動隱藏
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
var alerts = document.querySelectorAll('.alert');
|
|
alerts.forEach(function (alert) {
|
|
setTimeout(function () {
|
|
alert.classList.add('fade');
|
|
setTimeout(function () {
|
|
alert.remove();
|
|
}, 150);
|
|
}, 3000);
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %}
|