From 70e813242998229e97072cecd34babc4f8b43d85 Mon Sep 17 00:00:00 2001 From: gww Date: Tue, 27 Jan 2026 12:28:21 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=E5=AF=86=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/profile.tsx | 268 +-------------------------- components/ChangePasswordModal.tsx | 285 +++++++++++++++++++++++++++++ 2 files changed, 291 insertions(+), 262 deletions(-) create mode 100644 components/ChangePasswordModal.tsx diff --git a/app/profile.tsx b/app/profile.tsx index a6711b8..f9fcafa 100644 --- a/app/profile.tsx +++ b/app/profile.tsx @@ -7,6 +7,7 @@ import React, { useEffect, useState } from 'react' import { ScrollView } from 'react-native' import { imgPicker } from '@/@share/apis' +import ChangePasswordModal from '@/components/ChangePasswordModal' import { authClient } from '@/lib/auth' import { userStore } from '@/stores' import { uploadFile } from '@/utils' @@ -55,267 +56,6 @@ function EditNicknameModal({ initialName, onConfirm, onCancel }: EditNicknameMod ) } -type ChangePasswordModalProps = { - onConfirm: (currentPassword: string, newPassword: string) => void | Promise - onCancel: () => void -} - -// 验证密码强度 -const validatePassword = (password: string): string | null => { - if (!password) { - return '请输入新密码' - } - if (password.length < 6) { - return '密码长度至少6位' - } - // 检查是否是无效密码:全是相同字符 - if (/^(.)\1+$/.test(password)) { - return '密码不能全是相同字符' - } - // 检查是否是连续数字(如 123456, 654321) - const isSequentialNumbers = (str: string): boolean => { - for (let i = 0; i < str.length - 1; i++) { - const current = parseInt(str[i], 10) - const next = parseInt(str[i + 1], 10) - if (isNaN(current) || isNaN(next)) return false - if (Math.abs(current - next) !== 1) return false - } - return true - } - // 检查是否是连续字母(如 abcdef, fedcba) - const isSequentialLetters = (str: string): boolean => { - const lowerStr = str.toLowerCase() - for (let i = 0; i < lowerStr.length - 1; i++) { - const current = lowerStr.charCodeAt(i) - const next = lowerStr.charCodeAt(i + 1) - if (Math.abs(current - next) !== 1) return false - } - return true - } - if (isSequentialNumbers(password) || isSequentialLetters(password)) { - return '密码不能是连续字符' - } - // 检查是否是简单密码(如 111111, 000000, aaaaaa) - const commonWeakPasswords = ['111111', '222222', '333333', '444444', '555555', '666666', '777777', '888888', '999999', '000000', 'aaaaaa', 'bbbbbb', 'cccccc', 'dddddd', 'eeeeee', 'ffffff', '123456', '654321', 'abcdef', 'fedcba'] - if (commonWeakPasswords.includes(password.toLowerCase())) { - return '密码过于简单,请使用更复杂的密码' - } - return null -} - -function ChangePasswordModal({ onConfirm, onCancel }: ChangePasswordModalProps) { - const [currentPassword, setCurrentPassword] = useState('') - const [newPassword, setNewPassword] = useState('') - const [confirmPassword, setConfirmPassword] = useState('') - const [showCurrentPassword, setShowCurrentPassword] = useState(false) - const [showNewPassword, setShowNewPassword] = useState(false) - const [showConfirmPassword, setShowConfirmPassword] = useState(false) - const [loading, setLoading] = useState(false) - const [errors, setErrors] = useState<{ - currentPassword?: string - newPassword?: string - confirmPassword?: string - }>({}) - - const validateCurrentPassword = (value: string) => { - if (!value) { - setErrors((prev) => ({ ...prev, currentPassword: '请输入当前密码' })) - } else { - setErrors((prev) => { - const newErrors = { ...prev } - delete newErrors.currentPassword - return newErrors - }) - } - } - - const validateNewPassword = (value: string) => { - const error = validatePassword(value) - if (error) { - setErrors((prev) => ({ ...prev, newPassword: error })) - } else { - setErrors((prev) => { - const newErrors = { ...prev } - delete newErrors.newPassword - return newErrors - }) - // 如果确认密码已输入,重新验证确认密码 - if (confirmPassword) { - validateConfirmPassword(confirmPassword, value) - } - } - } - - const validateConfirmPassword = (value: string, newPwd = newPassword) => { - if (!value) { - setErrors((prev) => ({ ...prev, confirmPassword: '请再次输入新密码' })) - } else if (value !== newPwd) { - setErrors((prev) => ({ ...prev, confirmPassword: '两次密码输入不一致' })) - } else { - setErrors((prev) => { - const newErrors = { ...prev } - delete newErrors.confirmPassword - return newErrors - }) - } - } - - const handleConfirm = async () => { - if (loading) return - - // 验证所有字段并收集错误 - const newErrors: typeof errors = {} - - if (!currentPassword) { - newErrors.currentPassword = '请输入当前密码' - } - - const newPasswordError = validatePassword(newPassword) - if (newPasswordError) { - newErrors.newPassword = newPasswordError - } - - if (!confirmPassword) { - newErrors.confirmPassword = '请再次输入新密码' - } else if (confirmPassword !== newPassword) { - newErrors.confirmPassword = '两次密码输入不一致' - } - - // 设置错误状态 - setErrors(newErrors) - - // 如果有错误,不继续提交 - if (Object.keys(newErrors).length > 0) { - return - } - - setLoading(true) - try { - await onConfirm(currentPassword, newPassword) - } finally { - setLoading(false) - } - } - - return ( - - - 当前密码 - - { - setCurrentPassword(text) - if (errors.currentPassword) { - validateCurrentPassword(text) - } - }} - onBlur={() => validateCurrentPassword(currentPassword)} - secureTextEntry={!showCurrentPassword} - /> - setShowCurrentPassword(!showCurrentPassword)} - > - - - - {errors.currentPassword && ( - {errors.currentPassword} - )} - - - 新密码 - - { - setNewPassword(text) - if (errors.newPassword) { - validateNewPassword(text) - } - }} - onBlur={() => validateNewPassword(newPassword)} - secureTextEntry={!showNewPassword} - /> - setShowNewPassword(!showNewPassword)} - > - - - - {errors.newPassword && ( - {errors.newPassword} - )} - - - 确认新密码 - - { - setConfirmPassword(text) - if (errors.confirmPassword) { - validateConfirmPassword(text) - } - }} - onBlur={() => validateConfirmPassword(confirmPassword)} - secureTextEntry={!showConfirmPassword} - /> - setShowConfirmPassword(!showConfirmPassword)} - > - - - - {errors.confirmPassword && ( - {errors.confirmPassword} - )} - - - } - title="修改密码" - onCancel={onCancel} - onConfirm={handleConfirm} - /> - ) -} - export default observer(function ProfilePage() { const { user } = userStore @@ -403,7 +143,11 @@ export default observer(function ProfilePage() { return } Toast.hideModal() - Toast.show({ title: '密码已修改' }) + Toast.show({ title: '密码已修改,请重新登录' }) + // 退出登录 + await userStore.signOut() + // 跳转到登录页 + router.replace('/auth') } catch (e) { Toast.show({ title: '修改失败,请重试' }) } diff --git a/components/ChangePasswordModal.tsx b/components/ChangePasswordModal.tsx new file mode 100644 index 0000000..cfd689c --- /dev/null +++ b/components/ChangePasswordModal.tsx @@ -0,0 +1,285 @@ +import { Ionicons } from '@expo/vector-icons' +import { Block, ConfirmModal, Input, Text } from '@share/components' +import React, { useState } from 'react' + +export type ChangePasswordModalProps = { + onConfirm: (currentPassword: string, newPassword: string) => void | Promise + onCancel: () => void +} + +// 验证密码强度 +const validatePassword = (password: string): string | null => { + if (!password) { + return '请输入新密码' + } + if (password.length < 6) { + return '密码长度至少6位' + } + // 检查是否是无效密码:全是相同字符 + if (/^(.)\1+$/.test(password)) { + return '密码不能全是相同字符' + } + // 检查是否是连续数字(如 123456, 654321) + const isSequentialNumbers = (str: string): boolean => { + for (let i = 0; i < str.length - 1; i++) { + const current = parseInt(str[i], 10) + const next = parseInt(str[i + 1], 10) + if (isNaN(current) || isNaN(next)) return false + if (Math.abs(current - next) !== 1) return false + } + return true + } + // 检查是否是连续字母(如 abcdef, fedcba) + const isSequentialLetters = (str: string): boolean => { + const lowerStr = str.toLowerCase() + for (let i = 0; i < lowerStr.length - 1; i++) { + const current = lowerStr.charCodeAt(i) + const next = lowerStr.charCodeAt(i + 1) + if (Math.abs(current - next) !== 1) return false + } + return true + } + if (isSequentialNumbers(password) || isSequentialLetters(password)) { + return '密码不能是连续字符' + } + // 检查是否是简单密码(如 111111, 000000, aaaaaa) + const commonWeakPasswords = [ + '111111', + '222222', + '333333', + '444444', + '555555', + '666666', + '777777', + '888888', + '999999', + '000000', + 'aaaaaa', + 'bbbbbb', + 'cccccc', + 'dddddd', + 'eeeeee', + 'ffffff', + '123456', + '654321', + 'abcdef', + 'fedcba', + ] + if (commonWeakPasswords.includes(password.toLowerCase())) { + return '密码过于简单,请使用更复杂的密码' + } + return null +} + +export default function ChangePasswordModal({ onConfirm, onCancel }: ChangePasswordModalProps) { + const [currentPassword, setCurrentPassword] = useState('') + const [newPassword, setNewPassword] = useState('') + const [confirmPassword, setConfirmPassword] = useState('') + const [showCurrentPassword, setShowCurrentPassword] = useState(false) + const [showNewPassword, setShowNewPassword] = useState(false) + const [showConfirmPassword, setShowConfirmPassword] = useState(false) + const [loading, setLoading] = useState(false) + const [errors, setErrors] = useState<{ + currentPassword?: string + newPassword?: string + confirmPassword?: string + }>({}) + + const validateCurrentPassword = (value: string) => { + if (!value) { + setErrors((prev) => ({ ...prev, currentPassword: '请输入当前密码' })) + } else { + setErrors((prev) => { + const newErrors = { ...prev } + delete newErrors.currentPassword + return newErrors + }) + } + } + + const validateNewPassword = (value: string) => { + const error = validatePassword(value) + if (error) { + setErrors((prev) => ({ ...prev, newPassword: error })) + } else { + setErrors((prev) => { + const newErrors = { ...prev } + delete newErrors.newPassword + return newErrors + }) + // 如果确认密码已输入,重新验证确认密码 + if (confirmPassword) { + validateConfirmPassword(confirmPassword, value) + } + } + } + + const validateConfirmPassword = (value: string, newPwd = newPassword) => { + if (!value) { + setErrors((prev) => ({ ...prev, confirmPassword: '请再次输入新密码' })) + } else if (value !== newPwd) { + setErrors((prev) => ({ ...prev, confirmPassword: '两次密码输入不一致' })) + } else { + setErrors((prev) => { + const newErrors = { ...prev } + delete newErrors.confirmPassword + return newErrors + }) + } + } + + const handleConfirm = async () => { + if (loading) return + + // 验证所有字段并收集错误 + const newErrors: typeof errors = {} + + if (!currentPassword) { + newErrors.currentPassword = '请输入当前密码' + } + + const newPasswordError = validatePassword(newPassword) + if (newPasswordError) { + newErrors.newPassword = newPasswordError + } + + if (!confirmPassword) { + newErrors.confirmPassword = '请再次输入新密码' + } else if (confirmPassword !== newPassword) { + newErrors.confirmPassword = '两次密码输入不一致' + } + + // 设置错误状态 + setErrors(newErrors) + + // 如果有错误,不继续提交 + if (Object.keys(newErrors).length > 0) { + return + } + + setLoading(true) + try { + await onConfirm(currentPassword, newPassword) + } finally { + setLoading(false) + } + } + + return ( + + + 当前密码 + + { + setCurrentPassword(text) + if (errors.currentPassword) { + validateCurrentPassword(text) + } + }} + onBlur={() => validateCurrentPassword(currentPassword)} + secureTextEntry={!showCurrentPassword} + /> + setShowCurrentPassword(!showCurrentPassword)} + > + + + + {errors.currentPassword && ( + {errors.currentPassword} + )} + + + 新密码 + + { + setNewPassword(text) + if (errors.newPassword) { + validateNewPassword(text) + } + }} + onBlur={() => validateNewPassword(newPassword)} + secureTextEntry={!showNewPassword} + /> + setShowNewPassword(!showNewPassword)} + > + + + + {errors.newPassword && ( + {errors.newPassword} + )} + + + 确认新密码 + + { + setConfirmPassword(text) + if (errors.confirmPassword) { + validateConfirmPassword(text) + } + }} + onBlur={() => validateConfirmPassword(confirmPassword)} + secureTextEntry={!showConfirmPassword} + /> + setShowConfirmPassword(!showConfirmPassword)} + > + + + + {errors.confirmPassword && ( + {errors.confirmPassword} + )} + + + } + title="修改密码" + onCancel={onCancel} + onConfirm={handleConfirm} + /> + ) +}