diff --git a/Login/.vscode/settings.json b/Login/.vscode/settings.json new file mode 100644 index 0000000..8821a20 --- /dev/null +++ b/Login/.vscode/settings.json @@ -0,0 +1,62 @@ +{ + "files.associations": { + "cctype": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "*.tcc": "cpp", + "chrono": "cpp", + "codecvt": "cpp", + "compare": "cpp", + "concepts": "cpp", + "cstdint": "cpp", + "deque": "cpp", + "forward_list": "cpp", + "map": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "ratio": "cpp", + "string": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "new": "cpp", + "ostream": "cpp", + "ranges": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "stop_token": "cpp", + "streambuf": "cpp", + "thread": "cpp", + "cinttypes": "cpp", + "typeinfo": "cpp", + "valarray": "cpp" + } +} \ No newline at end of file diff --git a/Login/login.cpp b/Login/login.cpp index 0a7e92a..cf84912 100644 --- a/Login/login.cpp +++ b/Login/login.cpp @@ -510,9 +510,13 @@ extern "C" LOGIN_EXPORT int changePassword(int user_id, const char *old_password return -1; } + if (old_password_str == new_password_str) { + return -2; + } + const char *xnCorePath = std::getenv("XNCore"); if (!xnCorePath) { - return -1; + return -3; } fs::path dbPath = fs::path(xnCorePath) / "database" / "UserInfo.db"; @@ -520,7 +524,7 @@ extern "C" LOGIN_EXPORT int changePassword(int user_id, const char *old_password { sqlite3 *db; if (sqlite3_open(dbPath.string().c_str(), &db) != SQLITE_OK) { - return -1; + return -3; } // 首先验证旧密码 @@ -543,7 +547,7 @@ extern "C" LOGIN_EXPORT int changePassword(int user_id, const char *old_password if (encryptedOldPassword != storedPassword) { sqlite3_finalize(stmt); sqlite3_close(db); - return -3; // 旧密码错误 + return -4; // 旧密码错误 } // 生成新的加密密码 @@ -570,16 +574,16 @@ extern "C" LOGIN_EXPORT int changePassword(int user_id, const char *old_password // 用户不存在 sqlite3_finalize(stmt); sqlite3_close(db); - return -2; // 用户不存在 + return -5; // 用户不存在 } } sqlite3_close(db); } - return -1; // Default error return + return -6; // Default error return } catch (const std::exception &) { - return -1; + return -3; } } diff --git a/Release/database/UserInfo.db b/Release/database/UserInfo.db index 2b0b402..64d9480 100644 Binary files a/Release/database/UserInfo.db and b/Release/database/UserInfo.db differ diff --git a/Release/database/XNSim.db b/Release/database/XNSim.db index 5fae359..b7f0965 100644 Binary files a/Release/database/XNSim.db and b/Release/database/XNSim.db differ diff --git a/XNSimHtml/assets/icons/png/password.png b/XNSimHtml/assets/icons/png/password.png new file mode 100644 index 0000000..aca08b4 Binary files /dev/null and b/XNSimHtml/assets/icons/png/password.png differ diff --git a/XNSimHtml/components/profile-center.js b/XNSimHtml/components/profile-center.js index 56e57f5..c0c374a 100644 --- a/XNSimHtml/components/profile-center.js +++ b/XNSimHtml/components/profile-center.js @@ -65,6 +65,11 @@ class ProfileCenter extends HTMLElement { display: inline-block; } + .avatar-wrapper { + position: relative; + display: inline-block; + } + .avatar { width: 100px; height: 100px; @@ -78,12 +83,12 @@ class ProfileCenter extends HTMLElement { .avatar-edit-btn { position: absolute; - bottom: 15px; - right: 0; - width: 32px; - height: 32px; + bottom: 5px; + right: 5px; + width: 28px; + height: 28px; border-radius: 50%; - background: rgba(255,255,255,0.9); + background: rgba(255,255,255,0.95); border: 2px solid #667eea; cursor: pointer; display: flex; @@ -91,6 +96,7 @@ class ProfileCenter extends HTMLElement { justify-content: center; transition: all 0.3s ease; box-shadow: 0 2px 8px rgba(0,0,0,0.2); + z-index: 5; } .avatar-edit-btn:hover { @@ -100,9 +106,9 @@ class ProfileCenter extends HTMLElement { } .avatar-edit-btn img { - width: 16px; - height: 16px; - opacity: 0.7; + width: 14px; + height: 14px; + opacity: 0.8; } .avatar-edit-btn:hover img { @@ -319,6 +325,131 @@ class ProfileCenter extends HTMLElement { color: #6c757d; } + .modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; + } + + .modal-overlay.active { + opacity: 1; + visibility: visible; + } + + .modal { + background: white; + border-radius: 12px; + padding: 24px; + width: 90%; + max-width: 400px; + box-shadow: 0 10px 30px rgba(0,0,0,0.2); + transform: scale(0.9); + transition: transform 0.3s ease; + } + + .modal-overlay.active .modal { + transform: scale(1); + } + + .modal-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; + } + + .modal-title { + font-size: 18px; + font-weight: 600; + color: #333; + } + + .modal-close { + background: none; + border: none; + font-size: 20px; + cursor: pointer; + color: #6c757d; + padding: 0; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + } + + .modal-close:hover { + color: #333; + } + + .form-group { + margin-bottom: 16px; + } + + .form-label { + display: block; + margin-bottom: 6px; + font-weight: 500; + color: #333; + } + + .form-input { + width: 100%; + padding: 10px 12px; + border: 1px solid #ced4da; + border-radius: 6px; + font-size: 14px; + box-sizing: border-box; + } + + .form-input:focus { + outline: none; + border-color: #667eea; + box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.1); + } + + .modal-actions { + display: flex; + gap: 12px; + justify-content: flex-end; + margin-top: 24px; + } + + .btn-small { + padding: 8px 16px; + font-size: 14px; + } + + .modal-message { + margin-bottom: 16px; + padding: 12px; + border-radius: 6px; + font-size: 14px; + text-align: center; + } + + .modal-message.error { + background: #f8d7da; + color: #721c24; + border: 1px solid #f5c6cb; + } + + .modal-message.success { + background: #d4edda; + color: #155724; + border: 1px solid #c3e6cb; + } + @media (max-width: 768px) { :host { padding: 10px; @@ -340,9 +471,11 @@ class ProfileCenter extends HTMLElement {
- 用户头像 -
- 编辑头像 +
+ 用户头像 +
+ 编辑头像 +
@@ -414,6 +547,10 @@ class ProfileCenter extends HTMLElement { 编辑 编辑信息 +
+ + + `; } @@ -515,12 +681,14 @@ class ProfileCenter extends HTMLElement { const editBtn = this.shadowRoot.getElementById('editBtn'); const saveBtn = this.shadowRoot.getElementById('saveBtn'); const cancelBtn = this.shadowRoot.getElementById('cancelBtn'); + const changePasswordBtn = this.shadowRoot.getElementById('changePasswordBtn'); const avatarEditBtn = this.shadowRoot.getElementById('avatarEditBtn'); const avatarFileInput = this.shadowRoot.getElementById('avatarFileInput'); editBtn.addEventListener('click', () => this.startEditing()); saveBtn.addEventListener('click', () => this.saveChanges()); cancelBtn.addEventListener('click', () => this.cancelEditing()); + changePasswordBtn.addEventListener('click', () => this.showChangePasswordModal()); // 头像编辑按钮点击事件 avatarEditBtn.addEventListener('click', () => { @@ -750,6 +918,118 @@ class ProfileCenter extends HTMLElement { img.src = url; }); } + + showChangePasswordModal() { + const modal = this.shadowRoot.getElementById('changePasswordModal'); + modal.classList.add('active'); + + // 添加模态框事件监听器 + this.addPasswordModalListeners(); + } + + addPasswordModalListeners() { + const modal = this.shadowRoot.getElementById('changePasswordModal'); + const closeBtn = this.shadowRoot.getElementById('closePasswordModal'); + const cancelBtn = this.shadowRoot.getElementById('cancelPasswordChange'); + const form = this.shadowRoot.getElementById('changePasswordForm'); + + // 关闭按钮事件 + closeBtn.addEventListener('click', () => this.hideChangePasswordModal()); + + // 取消按钮事件 + cancelBtn.addEventListener('click', () => this.hideChangePasswordModal()); + + // 点击模态框背景关闭 + modal.addEventListener('click', (e) => { + if (e.target === modal) { + this.hideChangePasswordModal(); + } + }); + + // 表单提交事件 + form.addEventListener('submit', (e) => { + e.preventDefault(); + this.handlePasswordChange(); + }); + } + + hideChangePasswordModal() { + const modal = this.shadowRoot.getElementById('changePasswordModal'); + modal.classList.remove('active'); + + // 清空表单 + const form = this.shadowRoot.getElementById('changePasswordForm'); + form.reset(); + + // 隐藏消息 + const messageElement = this.shadowRoot.getElementById('passwordModalMessage'); + messageElement.style.display = 'none'; + } + + async handlePasswordChange() { + const oldPassword = this.shadowRoot.getElementById('oldPassword').value; + const newPassword = this.shadowRoot.getElementById('newPassword').value; + const confirmPassword = this.shadowRoot.getElementById('confirmPassword').value; + + // 验证输入 + if (!oldPassword || !newPassword || !confirmPassword) { + this.showPasswordModalMessage('请填写所有密码字段', 'error'); + return; + } + + if (newPassword !== confirmPassword) { + this.showPasswordModalMessage('新密码和确认密码不匹配', 'error'); + return; + } + + if (newPassword.length < 6) { + this.showPasswordModalMessage('新密码长度至少6位', 'error'); + return; + } + + try { + const response = await fetch('/api/change-password', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + userId: this.userInfo.id, + oldPassword: oldPassword, + newPassword: newPassword + }), + credentials: 'include' + }); + + const result = await response.json(); + + if (result.success) { + this.showPasswordModalMessage('密码修改成功', 'success'); + setTimeout(() => { + this.hideChangePasswordModal(); + }, 1500); + } else { + this.showPasswordModalMessage('密码修改失败: ' + result.message, 'error'); + } + } catch (error) { + console.error('修改密码失败:', error); + this.showPasswordModalMessage('修改密码失败', 'error'); + } + } + + showPasswordModalMessage(message, type) { + const messageElement = this.shadowRoot.getElementById('passwordModalMessage'); + messageElement.textContent = message; + messageElement.className = `modal-message ${type}`; + messageElement.style.display = 'block'; + + // 如果是成功消息,3秒后自动隐藏 + if (type === 'success') { + setTimeout(() => { + messageElement.style.display = 'none'; + }, 3000); + } + } } customElements.define('profile-center', ProfileCenter); \ No newline at end of file diff --git a/XNSimHtml/routes/auth.js b/XNSimHtml/routes/auth.js index 057a0a4..d40f7dd 100644 --- a/XNSimHtml/routes/auth.js +++ b/XNSimHtml/routes/auth.js @@ -230,6 +230,16 @@ router.post('/change-password', (req, res) => { if (result === 0) { res.json({ success: true, message: '密码修改成功' }); + } else if(result === -1){ + res.status(400).json({ success: false, message: '新密码或旧密码为空' }); + } else if(result === -2){ + res.status(400).json({ success: false, message: '新密码与旧密码相同' }); + } else if(result === -3){ + res.status(400).json({ success: false, message: '内部错误' }); + } else if(result === -4){ + res.status(400).json({ success: false, message: '旧密码错误' }); + } else if(result === -5){ + res.status(400).json({ success: false, message: '用户不存在' }); } else { res.status(400).json({ success: false, message: '密码修改失败', error: `错误码:${result}` }); }