first commit

This commit is contained in:
chz
2026-01-15 15:05:29 +08:00
commit 0ec405e137
66 changed files with 8572 additions and 0 deletions
+375
View File
@@ -0,0 +1,375 @@
// pages/edit-profile/index.js
const { userApi } = require("../../utils/api");
const AuthUtil = require("../../utils/auth");
Page({
/**
* 页面的初始数据
*/
data: {
username: '',
nickname: '',
originalUsername: '',
originalNickname: '',
loading: false,
// 密码相关
hasPassword: false, // 用户是否已设置密码
oldPassword: '',
newPassword: '',
confirmPassword: '',
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
this.loadUserInfo();
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
// 每次显示时刷新用户信息
this.loadUserInfo();
},
/**
* 加载用户信息
*/
loadUserInfo() {
const userInfo = AuthUtil.getUserInfo();
// 对于编辑页面,为了确保has_password字段的准确性,总是从服务器获取最新信息
// 但先使用本地信息快速显示,避免页面闪烁
if (userInfo && userInfo.username) {
this.setData({
username: userInfo.username || '',
nickname: userInfo.nickname || '',
originalUsername: userInfo.username || '',
originalNickname: userInfo.nickname || '',
hasPassword: !!(userInfo.has_password), // 使用本地缓存的has_password
});
}
// 总是从服务器获取最新信息,确保has_password字段准确
this.fetchUserInfo();
},
/**
* 从服务器获取用户信息
*/
fetchUserInfo() {
wx.showLoading({ title: '加载中...' });
userApi.getCurrentUser()
.then(res => {
if ((res.code === 1 || res.code === 200) && res.data && res.data.user) {
const user = res.data.user;
this.setData({
username: user.username || '',
nickname: user.nickname || '',
originalUsername: user.username || '',
originalNickname: user.nickname || '',
hasPassword: !!(user.has_password || user.password), // 判断用户是否已设置密码
});
// 更新本地存储
AuthUtil.setUserInfo(user);
} else {
wx.showToast({
title: '获取用户信息失败',
icon: 'none'
});
}
})
.catch(err => {
wx.showToast({
title: '获取用户信息失败',
icon: 'none'
});
})
.finally(() => {
wx.hideLoading();
});
},
/**
* 用户名输入变化
*/
onUsernameChange(e) {
this.setData({
username: e.detail.value,
});
},
/**
* 昵称输入变化
*/
onNicknameChange(e) {
this.setData({
nickname: e.detail.value,
});
},
/**
* 当前密码输入变化
*/
onOldPasswordChange(e) {
this.setData({
oldPassword: e.detail.value,
});
},
/**
* 新密码输入变化
*/
onNewPasswordChange(e) {
this.setData({
newPassword: e.detail.value,
});
},
/**
* 确认密码输入变化
*/
onConfirmPasswordChange(e) {
this.setData({
confirmPassword: e.detail.value,
});
},
/**
* 验证密码输入
*/
validatePassword() {
const { hasPassword, oldPassword, newPassword, confirmPassword } = this.data;
// 如果没有输入新密码,说明不想修改密码
if (!newPassword.trim() && !confirmPassword.trim()) {
return { valid: true, changePassword: false };
}
// 如果用户已有密码,必须输入当前密码
if (hasPassword && !oldPassword.trim()) {
wx.showToast({
title: '请输入当前密码',
icon: 'none'
});
return { valid: false, changePassword: false };
}
// 新密码长度检查
if (newPassword.length < 6 || newPassword.length > 50) {
wx.showToast({
title: '新密码长度必须在6-50字符之间',
icon: 'none'
});
return { valid: false, changePassword: false };
}
// 确认密码检查
if (newPassword !== confirmPassword) {
wx.showToast({
title: '确认密码与新密码不一致',
icon: 'none'
});
return { valid: false, changePassword: false };
}
return { valid: true, changePassword: true };
},
/**
* 验证输入
*/
validateInput() {
const { username, nickname } = this.data;
if (!username.trim()) {
wx.showToast({
title: '请输入用户名',
icon: 'none'
});
return false;
}
if (username.length < 3 || username.length > 50) {
wx.showToast({
title: '用户名长度必须在3-50字符之间',
icon: 'none'
});
return false;
}
// 检查用户名格式(只能包含字母和数字)
const usernameRegex = /^[a-zA-Z0-9]+$/;
if (!usernameRegex.test(username)) {
wx.showToast({
title: '用户名只能包含字母和数字',
icon: 'none'
});
return false;
}
if (!nickname.trim()) {
wx.showToast({
title: '请输入昵称',
icon: 'none'
});
return false;
}
if (nickname.length < 2 || nickname.length > 50) {
wx.showToast({
title: '昵称长度必须在2-50字符之间',
icon: 'none'
});
return false;
}
return true;
},
/**
* 检查是否有修改
*/
hasChanges() {
const { username, nickname, originalUsername, originalNickname, newPassword } = this.data;
return username !== originalUsername ||
nickname !== originalNickname ||
newPassword.trim() !== '';
},
/**
* 保存用户信息修改
*/
saveProfileChanges() {
const { username, nickname } = this.data;
return userApi.updateProfile({
username: username.trim(),
nickname: nickname.trim()
});
},
/**
* 保存密码修改
*/
savePasswordChanges() {
const { hasPassword, oldPassword, newPassword, confirmPassword } = this.data;
return userApi.changePassword(
hasPassword ? oldPassword : '',
newPassword,
confirmPassword
);
},
/**
* 保存修改
*/
handleSave() {
if (!this.validateInput()) {
return;
}
const passwordValidation = this.validatePassword();
if (!passwordValidation.valid) {
return;
}
if (!this.hasChanges()) {
wx.showToast({
title: '没有修改内容',
icon: 'none'
});
return;
}
this.setData({ loading: true });
// 检查是否需要修改个人信息
const { username, nickname, originalUsername, originalNickname } = this.data;
const profileChanged = username !== originalUsername || nickname !== originalNickname;
// 需要执行的保存操作
const saveOperations = [];
if (profileChanged) {
saveOperations.push(this.saveProfileChanges());
}
if (passwordValidation.changePassword) {
saveOperations.push(this.savePasswordChanges());
}
// 执行所有保存操作
Promise.all(saveOperations)
.then(results => {
// 检查所有操作是否都成功(支持code: 1和code: 200
const allSuccess = results.every(res => res.code === 1 || res.code === 200);
if (allSuccess) {
wx.showToast({
title: '保存成功',
icon: 'success'
});
// 如果有个人信息更新,更新本地用户信息
const profileResult = results.find(res => res.data && res.data.user);
if (profileResult) {
AuthUtil.setUserInfo(profileResult.data.user);
}
// 延时返回上一页
setTimeout(() => {
wx.navigateBack();
}, 1500);
} else {
// 找出失败的操作
const failedResults = results.filter(res => res.code !== 1);
const errorMsg = failedResults.map(res => res.msg).join('');
wx.showToast({
title: errorMsg || '保存失败',
icon: 'none'
});
}
})
.catch(err => {
wx.showToast({
title: err.message || '保存失败,请稍后重试',
icon: 'none'
});
})
.finally(() => {
this.setData({ loading: false });
});
},
/**
* 取消修改
*/
handleCancel() {
if (this.hasChanges()) {
wx.showModal({
title: '提示',
content: '确定要放弃修改吗?',
success: (res) => {
if (res.confirm) {
wx.navigateBack();
}
}
});
} else {
wx.navigateBack();
}
},
/**
* 页面卸载时的处理
*/
onUnload() {
// 如果有未保存的修改,可以在这里提示用户
}
});
+5
View File
@@ -0,0 +1,5 @@
{
"navigationBarTitleText": "编辑个人信息",
"navigationBarBackgroundColor": "#0052d9",
"navigationBarTextStyle": "white"
}
+106
View File
@@ -0,0 +1,106 @@
<!-- 编辑个人信息页面 -->
<view class="edit-profile-container">
<view class="form-section">
<view class="form-item">
<view class="form-label">
<text class="label-text">用户名</text>
</view>
<input
class="form-input"
placeholder="请输入用户名"
value="{{username}}"
bindinput="onUsernameChange"
maxlength="50"
/>
</view>
<view class="form-item">
<view class="form-label">
<text class="label-text">昵称</text>
</view>
<input
class="form-input"
placeholder="请输入昵称"
value="{{nickname}}"
bindinput="onNicknameChange"
maxlength="50"
/>
</view>
<view class="tips">
<text class="tips-text">用户名只能包含字母和数字,长度3-50字符</text>
</view>
</view>
<!-- 密码修改区域 -->
<view class="form-section">
<view class="section-title">
<text>修改密码</text>
<text class="section-subtitle">{{hasPassword ? '如不修改请留空' : '微信登录用户可设置密码'}}</text>
</view>
<view class="form-item" wx:if="{{hasPassword}}">
<view class="form-label">
<text class="label-text">当前密码</text>
</view>
<input
class="form-input"
type="password"
placeholder="请输入当前密码"
value="{{oldPassword}}"
bindinput="onOldPasswordChange"
maxlength="50"
/>
</view>
<view class="form-item">
<view class="form-label">
<text class="label-text">新密码</text>
</view>
<input
class="form-input"
type="password"
placeholder="{{hasPassword ? '请输入新密码' : '请设置密码'}}"
value="{{newPassword}}"
bindinput="onNewPasswordChange"
maxlength="50"
/>
</view>
<view class="form-item">
<view class="form-label">
<text class="label-text">确认密码</text>
</view>
<input
class="form-input"
type="password"
placeholder="请再次输入密码确认"
value="{{confirmPassword}}"
bindinput="onConfirmPasswordChange"
maxlength="50"
/>
</view>
<view class="tips">
<text class="tips-text">密码长度6-50字符,建议包含字母和数字</text>
</view>
</view>
<view class="button-section">
<button
class="save-btn {{loading ? 'loading' : ''}}"
bindtap="handleSave"
disabled="{{loading}}"
>
{{loading ? '保存中...' : '保存修改'}}
</button>
<button
class="cancel-btn"
bindtap="handleCancel"
>
取消
</button>
</view>
</view>
+128
View File
@@ -0,0 +1,128 @@
/* pages/edit-profile/index.wxss */
.edit-profile-container {
padding: 30rpx;
background-color: #f6f6f6;
min-height: 100vh;
box-sizing: border-box;
}
.form-section {
background-color: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 30rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.form-item {
margin-bottom: 40rpx;
}
.form-item:last-child {
margin-bottom: 0;
}
.form-label {
margin-bottom: 20rpx;
}
.label-text {
font-size: 30rpx;
color: #333;
font-weight: 500;
}
.form-input {
width: 100%;
height: 80rpx;
padding: 0 20rpx;
border: 2rpx solid #e0e0e0;
border-radius: 8rpx;
font-size: 32rpx;
color: #333;
background-color: #fff;
box-sizing: border-box;
}
.form-input:focus {
border-color: #0052d9;
}
.tips {
margin-top: 30rpx;
padding: 20rpx;
background-color: #f8f9fa;
border-radius: 8rpx;
border-left: 6rpx solid #0052d9;
}
.tips-text {
font-size: 24rpx;
color: #666;
line-height: 1.5;
}
.section-title {
margin-bottom: 30rpx;
padding-bottom: 20rpx;
border-bottom: 2rpx solid #f0f0f0;
}
.section-title text:first-child {
font-size: 32rpx;
color: #333;
font-weight: 500;
display: block;
margin-bottom: 8rpx;
}
.section-subtitle {
font-size: 24rpx;
color: #999;
}
.button-section {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.save-btn {
width: 100%;
height: 88rpx;
background-color: #0052d9;
color: #fff;
border: none;
border-radius: 12rpx;
font-size: 32rpx;
font-weight: 500;
display: flex;
align-items: center;
justify-content: center;
}
.save-btn.loading {
background-color: #ccc;
}
.save-btn:not(.loading):active {
background-color: #003ba3;
}
.cancel-btn {
width: 100%;
height: 88rpx;
background-color: #fff;
color: #666;
border: 2rpx solid #e0e0e0;
border-radius: 12rpx;
font-size: 32rpx;
display: flex;
align-items: center;
justify-content: center;
}
.cancel-btn:active {
background-color: #f5f5f5;
}