|
|
|
|
@ -8,104 +8,447 @@
|
|
|
|
|
</head>
|
|
|
|
|
<style>
|
|
|
|
|
body {
|
|
|
|
|
max-width: 600px;
|
|
|
|
|
padding: 24px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.profile-container {
|
|
|
|
|
max-width: 1000px;
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
padding: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.profile-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
height: 100%;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
justify-content: start;
|
|
|
|
|
gap: 2rem;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
margin-bottom: 24px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.profile-title {
|
|
|
|
|
font-size: 24px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: #333;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.refresh-btn {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: #666;
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
padding: 4px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
transition: all 0.2s ease;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.refresh-btn:hover {
|
|
|
|
|
color: #6c5ce7;
|
|
|
|
|
background: #f8f7ff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 用户头像卡片 */
|
|
|
|
|
.avatar-section {
|
|
|
|
|
background: linear-gradient(135deg, #6c5ce7, #5a4fcf);
|
|
|
|
|
color: white;
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
padding: 32px 24px;
|
|
|
|
|
margin-bottom: 24px;
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.avatar-container {
|
|
|
|
|
position: relative;
|
|
|
|
|
display: inline-block;
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.avatar-preview {
|
|
|
|
|
width: 80px;
|
|
|
|
|
height: 80px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
border: 4px solid rgba(255, 255, 255, 0.3);
|
|
|
|
|
transition: all 0.2s ease;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.avatar-placeholder {
|
|
|
|
|
width: 80px;
|
|
|
|
|
height: 80px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
background: rgba(255, 255, 255, 0.2);
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
font-size: 32px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
border: 4px solid rgba(255, 255, 255, 0.3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.avatar-info h3 {
|
|
|
|
|
font-size: 20px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
margin: 0 0 4px 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.avatar-info p {
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
opacity: 0.9;
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 表单样式 */
|
|
|
|
|
.profile-section {
|
|
|
|
|
background: white;
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
padding: 24px;
|
|
|
|
|
margin-bottom: 24px;
|
|
|
|
|
border: 1px solid #e0e0e0;
|
|
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.section-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.section-icon {
|
|
|
|
|
width: 24px;
|
|
|
|
|
height: 24px;
|
|
|
|
|
color: #6c5ce7;
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.section-title {
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: #333;
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.form-group {
|
|
|
|
|
margin-bottom: 15px;
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.form-group:last-child {
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
label {
|
|
|
|
|
.form-label {
|
|
|
|
|
display: block;
|
|
|
|
|
margin-bottom: 5px;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
color: #333;
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
input[type="text"],
|
|
|
|
|
input[type="email"],
|
|
|
|
|
input[type="tel"] {
|
|
|
|
|
.form-input {
|
|
|
|
|
width: 100%;
|
|
|
|
|
padding: 8px;
|
|
|
|
|
border: 1px solid #ddd;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
padding: 12px 16px;
|
|
|
|
|
border: 2px solid #e0e0e0;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
color: #333;
|
|
|
|
|
background: white;
|
|
|
|
|
transition: all 0.2s ease;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.avatar-preview {
|
|
|
|
|
width: 100px;
|
|
|
|
|
height: 100px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
.form-input:focus {
|
|
|
|
|
outline: none;
|
|
|
|
|
border-color: #6c5ce7;
|
|
|
|
|
box-shadow: 0 0 0 2px rgba(108, 92, 231, 0.1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.form-input:disabled {
|
|
|
|
|
background: #f8f9fa;
|
|
|
|
|
color: #666;
|
|
|
|
|
cursor: not-allowed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.form-description {
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
color: #666;
|
|
|
|
|
margin-top: 4px;
|
|
|
|
|
line-height: 1.4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 头像输入特殊样式 */
|
|
|
|
|
.avatar-input-group {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.avatar-input {
|
|
|
|
|
flex: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.avatar-preview-small {
|
|
|
|
|
width: 48px;
|
|
|
|
|
height: 48px;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
border: 2px solid #e0e0e0;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.avatar-placeholder-small {
|
|
|
|
|
width: 48px;
|
|
|
|
|
height: 48px;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
background: #f0f0f0;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
color: #999;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
border: 2px solid #e0e0e0;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 保存按钮区域 */
|
|
|
|
|
.save-section {
|
|
|
|
|
background: #f8f9fa;
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
padding: 20px 24px;
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: center;
|
|
|
|
|
border: 1px solid #e0e0e0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.save-info {
|
|
|
|
|
color: #666;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn {
|
|
|
|
|
background-color: #4CAF50;
|
|
|
|
|
.save-info.changed {
|
|
|
|
|
color: #6c5ce7;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.save-btn {
|
|
|
|
|
padding: 12px 24px;
|
|
|
|
|
background: #6c5ce7;
|
|
|
|
|
color: white;
|
|
|
|
|
padding: 10px 15px;
|
|
|
|
|
border: none;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: all 0.2s ease;
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.save-btn.enabled {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
pointer-events: auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn:hover {
|
|
|
|
|
background-color: #45a049;
|
|
|
|
|
.save-btn:hover.enabled {
|
|
|
|
|
background: #5a4fcf;
|
|
|
|
|
transform: translateY(-1px);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.error-message {
|
|
|
|
|
color: red;
|
|
|
|
|
.save-btn.saving {
|
|
|
|
|
background: #28a745;
|
|
|
|
|
cursor: not-allowed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 消息提示 */
|
|
|
|
|
.message {
|
|
|
|
|
position: fixed;
|
|
|
|
|
top: 20px;
|
|
|
|
|
right: 20px;
|
|
|
|
|
padding: 12px 20px;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
margin-top: 5px;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
|
|
|
transform: translateX(400px);
|
|
|
|
|
transition: transform 0.3s ease;
|
|
|
|
|
z-index: 1000;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message.show {
|
|
|
|
|
transform: translateX(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message.error {
|
|
|
|
|
background: #dc3545;
|
|
|
|
|
color: white;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message.success {
|
|
|
|
|
background: #28a745;
|
|
|
|
|
color: white;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 加载状态 */
|
|
|
|
|
.loading-overlay {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
right: 0;
|
|
|
|
|
bottom: 0;
|
|
|
|
|
background: rgba(255, 255, 255, 0.8);
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
z-index: 10;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.loading-spinner {
|
|
|
|
|
color: #6c5ce7;
|
|
|
|
|
font-size: 24px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.section-loading {
|
|
|
|
|
position: relative;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@media (max-width: 768px) {
|
|
|
|
|
.profile-container {
|
|
|
|
|
padding: 0 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.profile-section {
|
|
|
|
|
padding: 20px 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.avatar-section {
|
|
|
|
|
padding: 24px 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.avatar-input-group {
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.save-section {
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
|
|
|
|
|
<body>
|
|
|
|
|
<h1 class="text-2xl text-center">个人信息修改</h1>
|
|
|
|
|
<div class="profile-container">
|
|
|
|
|
<!-- Header -->
|
|
|
|
|
<div class="profile-header">
|
|
|
|
|
<h1 class="profile-title">个人信息</h1>
|
|
|
|
|
<button class="refresh-btn" @click="loadUserData">
|
|
|
|
|
<i class="fa-solid fa-rotate-right"></i>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Avatar Section -->
|
|
|
|
|
<div class="avatar-section">
|
|
|
|
|
<div class="avatar-container">
|
|
|
|
|
<img v-if="user.icon" :src="user.icon" class="avatar-preview" alt="用户头像">
|
|
|
|
|
<div v-else class="avatar-placeholder">
|
|
|
|
|
{{ user.username ? user.username.charAt(0).toUpperCase() : 'U' }}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="avatar-info">
|
|
|
|
|
<h3>{{ user.nickname || user.username || '未设置昵称' }}</h3>
|
|
|
|
|
<p>用户ID: {{ user.id }}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Basic Info Section -->
|
|
|
|
|
<div class="profile-section section-loading" v-if="!isLoading">
|
|
|
|
|
<div class="section-header">
|
|
|
|
|
<i class="fa-solid fa-user section-icon"></i>
|
|
|
|
|
<h2 class="section-title">基本信息</h2>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
<label>用户名</label>
|
|
|
|
|
<input type="text" !value="user.username" @input="user.username = $event.target.value" placeholder="请输入用户名">
|
|
|
|
|
<label class="form-label">用户名</label>
|
|
|
|
|
<input type="text" class="form-input" !value="user.username"
|
|
|
|
|
@input="updateField('username', $event.target.value)" placeholder="请输入用户名">
|
|
|
|
|
<div class="form-description">用户名用于登录,建议使用英文或数字</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
<label>昵称</label>
|
|
|
|
|
<input type="text" !value="user.nickname" @input="user.nickname = $event.target.value" placeholder="请输入昵称">
|
|
|
|
|
<label class="form-label">昵称</label>
|
|
|
|
|
<input type="text" class="form-input" !value="user.nickname"
|
|
|
|
|
@input="updateField('nickname', $event.target.value)" placeholder="请输入昵称">
|
|
|
|
|
<div class="form-description">昵称将在页面中显示,可以使用中文</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
<label>头像URL</label>
|
|
|
|
|
<img v-if="user.icon" :src="user.icon" class="avatar-preview">
|
|
|
|
|
<input type="text" !value="user.icon" @input="user.icon = $event.target.value" placeholder="请输入头像URL">
|
|
|
|
|
<label class="form-label">头像URL</label>
|
|
|
|
|
<div class="avatar-input-group">
|
|
|
|
|
<div class="avatar-input">
|
|
|
|
|
<input type="text" class="form-input" !value="user.icon" @input="updateField('icon', $event.target.value)"
|
|
|
|
|
placeholder="请输入头像图片URL">
|
|
|
|
|
<div class="form-description">输入图片链接地址,支持jpg、png、gif格式</div>
|
|
|
|
|
</div>
|
|
|
|
|
<img v-if="user.icon" :src="user.icon" class="avatar-preview-small" alt="头像预览">
|
|
|
|
|
<div v-else class="avatar-placeholder-small">无</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Contact Info Section -->
|
|
|
|
|
<div class="profile-section section-loading" v-if="!isLoading">
|
|
|
|
|
<div class="section-header">
|
|
|
|
|
<i class="fa-solid fa-address-book section-icon"></i>
|
|
|
|
|
<h2 class="section-title">联系方式</h2>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
<label>电子邮箱</label>
|
|
|
|
|
<input type="email" !value="user.email" @input="user.email = $event.target.value" placeholder="请输入电子邮箱">
|
|
|
|
|
<label class="form-label">电子邮箱</label>
|
|
|
|
|
<input type="email" class="form-input" !value="user.email" @input="updateField('email', $event.target.value)"
|
|
|
|
|
placeholder="请输入电子邮箱">
|
|
|
|
|
<div class="form-description">用于接收重要通知和找回密码</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
|
<label>手机号码</label>
|
|
|
|
|
<input type="tel" !value="user.phone" @input="user.phone = $event.target.value" placeholder="请输入手机号码">
|
|
|
|
|
<label class="form-label">手机号码</label>
|
|
|
|
|
<input type="tel" class="form-input" !value="user.phone" @input="updateField('phone', $event.target.value)"
|
|
|
|
|
placeholder="请输入手机号码">
|
|
|
|
|
<div class="form-description">用于接收验证码和安全提醒</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Loading State -->
|
|
|
|
|
<div class="profile-section" v-if="isLoading">
|
|
|
|
|
<div class="loading-overlay">
|
|
|
|
|
<i class="fa-solid fa-spinner fa-spin loading-spinner"></i>
|
|
|
|
|
</div>
|
|
|
|
|
<div style="height: 200px;"></div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<button @click="saveProfile" class="btn">保存修改</button>
|
|
|
|
|
<!-- Save Section -->
|
|
|
|
|
<div class="save-section" v-if="!isLoading">
|
|
|
|
|
<div class="save-info" :class="{ changed: hasChanges }">
|
|
|
|
|
{{ hasChanges ? '您有未保存的更改' : '所有信息已保存' }}
|
|
|
|
|
</div>
|
|
|
|
|
<button class="save-btn" :class="{ enabled: hasChanges && !isSaving, saving: isSaving }" @click="saveProfile"
|
|
|
|
|
:disabled="!hasChanges || isSaving">
|
|
|
|
|
{{ isSaving ? '保存中...' : '保存修改' }}
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Messages -->
|
|
|
|
|
<div class="message error" :class="{ show: showErrorMessage }">
|
|
|
|
|
<i class="fa-solid fa-exclamation-circle"></i>
|
|
|
|
|
{{ errorMessage }}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div v-if="errorMessage" class="error-message">{{ errorMessage }}</div>
|
|
|
|
|
<div v-if="successMessage" style="color: green; margin-top: 10px;">{{ successMessage }}</div>
|
|
|
|
|
<div class="message success" :class="{ show: showSuccessMessage }">
|
|
|
|
|
<i class="fa-solid fa-check-circle"></i>
|
|
|
|
|
{{ successMessage }}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</body>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
// 初始化用户数据
|
|
|
|
|
|
|
|
|
|
user = {
|
|
|
|
|
id: $G.token?.body()?.uid,
|
|
|
|
|
username: '',
|
|
|
|
|
@ -114,17 +457,60 @@
|
|
|
|
|
email: "",
|
|
|
|
|
phone: "",
|
|
|
|
|
status: 0
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 原始用户数据,用于比较变更
|
|
|
|
|
originalUser = {}
|
|
|
|
|
|
|
|
|
|
errorMessage = "";
|
|
|
|
|
successMessage = "";
|
|
|
|
|
isLoading = false;
|
|
|
|
|
// UI状态
|
|
|
|
|
errorMessage = ""
|
|
|
|
|
successMessage = ""
|
|
|
|
|
isLoading = false
|
|
|
|
|
isSaving = false
|
|
|
|
|
hasChanges = false
|
|
|
|
|
showErrorMessage = false
|
|
|
|
|
showSuccessMessage = false
|
|
|
|
|
|
|
|
|
|
// 更新字段
|
|
|
|
|
updateField = (field, value) => {
|
|
|
|
|
user[field] = value
|
|
|
|
|
checkForChanges()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查是否有变更
|
|
|
|
|
checkForChanges = () => {
|
|
|
|
|
hasChanges = (
|
|
|
|
|
user.username !== originalUser.username ||
|
|
|
|
|
user.nickname !== originalUser.nickname ||
|
|
|
|
|
user.icon !== originalUser.icon ||
|
|
|
|
|
user.email !== originalUser.email ||
|
|
|
|
|
user.phone !== originalUser.phone
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 显示错误消息
|
|
|
|
|
showError = (message) => {
|
|
|
|
|
errorMessage = message
|
|
|
|
|
showErrorMessage = true
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
showErrorMessage = false
|
|
|
|
|
}, 5000)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 显示成功消息
|
|
|
|
|
showSuccess = (message) => {
|
|
|
|
|
successMessage = message
|
|
|
|
|
showSuccessMessage = true
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
showSuccessMessage = false
|
|
|
|
|
}, 3000)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 加载用户数据
|
|
|
|
|
loadUserData = async () => {
|
|
|
|
|
try {
|
|
|
|
|
isLoading = true;
|
|
|
|
|
const response = await $axios.get("/api/user/" + user.id);
|
|
|
|
|
isLoading = true
|
|
|
|
|
const response = await $axios.get("/api/user/" + user.id)
|
|
|
|
|
if (response) {
|
|
|
|
|
user = {
|
|
|
|
|
id: response.id,
|
|
|
|
|
@ -134,21 +520,26 @@
|
|
|
|
|
email: response.email || "",
|
|
|
|
|
phone: response.phone || "",
|
|
|
|
|
status: response.status || 0
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
// 保存原始数据
|
|
|
|
|
originalUser = JSON.parse(JSON.stringify(user))
|
|
|
|
|
hasChanges = false
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
errorMessage = "加载用户数据失败: " + error.message;
|
|
|
|
|
showError("加载用户数据失败: " + error.message)
|
|
|
|
|
} finally {
|
|
|
|
|
isLoading = false;
|
|
|
|
|
isLoading = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 保存修改
|
|
|
|
|
saveProfile = async () => {
|
|
|
|
|
try {
|
|
|
|
|
errorMessage = "";
|
|
|
|
|
successMessage = "";
|
|
|
|
|
isLoading = true;
|
|
|
|
|
if (!hasChanges || isSaving) return
|
|
|
|
|
|
|
|
|
|
errorMessage = ""
|
|
|
|
|
successMessage = ""
|
|
|
|
|
isSaving = true
|
|
|
|
|
|
|
|
|
|
// 准备更新数据
|
|
|
|
|
const updateData = {
|
|
|
|
|
@ -157,13 +548,12 @@
|
|
|
|
|
icon: user.icon || null,
|
|
|
|
|
email: user.email || null,
|
|
|
|
|
phone: user.phone || null
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 发送更新请求
|
|
|
|
|
const response = await $axios.patch("/api/user/" + user.id, updateData);
|
|
|
|
|
const response = await $axios.patch("/api/user/" + user.id, updateData)
|
|
|
|
|
|
|
|
|
|
if (response) {
|
|
|
|
|
successMessage = "个人信息更新成功!";
|
|
|
|
|
// 更新本地数据
|
|
|
|
|
user = {
|
|
|
|
|
...user,
|
|
|
|
|
@ -172,18 +562,41 @@
|
|
|
|
|
icon: response.icon || user.icon,
|
|
|
|
|
email: response.email || user.email,
|
|
|
|
|
phone: response.phone || user.phone
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 更新原始数据
|
|
|
|
|
originalUser = JSON.parse(JSON.stringify(user))
|
|
|
|
|
hasChanges = false
|
|
|
|
|
|
|
|
|
|
showSuccess("个人信息更新成功!")
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
errorMessage = "保存失败: " + error;
|
|
|
|
|
showError("保存失败: " + error)
|
|
|
|
|
} finally {
|
|
|
|
|
isLoading = false;
|
|
|
|
|
isSaving = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
loadUserData();
|
|
|
|
|
// 页面加载时获取用户数据
|
|
|
|
|
$data.loadUserData()
|
|
|
|
|
|
|
|
|
|
// 监听用户数据变化
|
|
|
|
|
$watch(() => {
|
|
|
|
|
if ($data.hasChanges) {
|
|
|
|
|
console.log('Profile has unsaved changes')
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 页面离开前提醒未保存的更改
|
|
|
|
|
window.addEventListener('beforeunload', (event) => {
|
|
|
|
|
if ($data.hasChanges) {
|
|
|
|
|
event.preventDefault()
|
|
|
|
|
event.returnValue = '您有未保存的更改,确定要离开页面吗?'
|
|
|
|
|
return event.returnValue
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
</html>
|
|
|
|
|
|