mirror of
https://github.com/Funkoala14/KnowledgeBase_OOIN.git
synced 2025-06-08 09:38:14 +08:00
[dev]add change password
This commit is contained in:
parent
01e60c5674
commit
98b7a08143
151
src/components/ChangePasswordModal.jsx
Normal file
151
src/components/ChangePasswordModal.jsx
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { changePasswordThunk } from '../store/auth/auth.thunk';
|
||||||
|
|
||||||
|
function ChangePasswordModal({ show, onClose }) {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
old_password: '',
|
||||||
|
new_password: '',
|
||||||
|
confirm_password: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const [errors, setErrors] = useState({});
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
const [submitted, setSubmitted] = useState(false);
|
||||||
|
|
||||||
|
const handleChange = (e) => {
|
||||||
|
const { name, value } = e.target;
|
||||||
|
setFormData({
|
||||||
|
...formData,
|
||||||
|
[name]: value.trim(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateForm = () => {
|
||||||
|
const newErrors = {};
|
||||||
|
|
||||||
|
if (!formData.old_password) {
|
||||||
|
newErrors.old_password = '请输入当前密码';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!formData.new_password) {
|
||||||
|
newErrors.new_password = '请输入新密码';
|
||||||
|
} else if (formData.new_password.length < 6) {
|
||||||
|
newErrors.new_password = '新密码长度不能少于6个字符';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!formData.confirm_password) {
|
||||||
|
newErrors.confirm_password = '请确认新密码';
|
||||||
|
} else if (formData.new_password !== formData.confirm_password) {
|
||||||
|
newErrors.confirm_password = '两次输入的密码不一致';
|
||||||
|
}
|
||||||
|
|
||||||
|
setErrors(newErrors);
|
||||||
|
return Object.keys(newErrors).length === 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setSubmitted(true);
|
||||||
|
|
||||||
|
if (validateForm()) {
|
||||||
|
setIsSubmitting(true);
|
||||||
|
try {
|
||||||
|
await dispatch(
|
||||||
|
changePasswordThunk({
|
||||||
|
old_password: formData.old_password,
|
||||||
|
new_password: formData.new_password,
|
||||||
|
})
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
// 成功后会由thunk中的代码重定向到登录页
|
||||||
|
navigate('/login');
|
||||||
|
} catch (error) {
|
||||||
|
// 错误处理已经在thunk中完成
|
||||||
|
console.error('Change password failed:', error);
|
||||||
|
} finally {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!show) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='modal show d-block' style={{ backgroundColor: 'rgba(0,0,0,0.5)' }}>
|
||||||
|
<div className='modal-dialog modal-dialog-centered'>
|
||||||
|
<div className='modal-content'>
|
||||||
|
<div className='modal-header border-0'>
|
||||||
|
<h5 className='modal-title'>修改密码</h5>
|
||||||
|
<button type='button' className='btn-close' onClick={onClose}></button>
|
||||||
|
</div>
|
||||||
|
<div className='modal-body'>
|
||||||
|
<form onSubmit={handleSubmit} noValidate>
|
||||||
|
<div className='mb-3'>
|
||||||
|
<label className='form-label'>当前密码</label>
|
||||||
|
<input
|
||||||
|
value={formData.old_password}
|
||||||
|
name='old_password'
|
||||||
|
type='password'
|
||||||
|
className={`form-control${submitted && errors.old_password ? ' is-invalid' : ''}`}
|
||||||
|
required
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
{submitted && errors.old_password && (
|
||||||
|
<div className='invalid-feedback'>{errors.old_password}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='mb-3'>
|
||||||
|
<label className='form-label'>新密码</label>
|
||||||
|
<input
|
||||||
|
value={formData.new_password}
|
||||||
|
name='new_password'
|
||||||
|
type='password'
|
||||||
|
className={`form-control${submitted && errors.new_password ? ' is-invalid' : ''}`}
|
||||||
|
required
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
{submitted && errors.new_password && (
|
||||||
|
<div className='invalid-feedback'>{errors.new_password}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='mb-3'>
|
||||||
|
<label className='form-label'>确认新密码</label>
|
||||||
|
<input
|
||||||
|
value={formData.confirm_password}
|
||||||
|
name='confirm_password'
|
||||||
|
type='password'
|
||||||
|
className={`form-control${
|
||||||
|
submitted && errors.confirm_password ? ' is-invalid' : ''
|
||||||
|
}`}
|
||||||
|
required
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
{submitted && errors.confirm_password && (
|
||||||
|
<div className='invalid-feedback'>{errors.confirm_password}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='modal-footer border-0'>
|
||||||
|
<button type='button' className='btn btn-outline-dark' onClick={onClose}>
|
||||||
|
取消
|
||||||
|
</button>
|
||||||
|
<button type='submit' className='btn btn-dark' disabled={isSubmitting}>
|
||||||
|
{isSubmitting ? '提交中...' : '修改密码'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChangePasswordModal;
|
@ -1,6 +1,7 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { updateProfileThunk } from '../store/auth/auth.thunk';
|
import { updateProfileThunk } from '../store/auth/auth.thunk';
|
||||||
|
import ChangePasswordModal from './ChangePasswordModal';
|
||||||
|
|
||||||
// 部门和组别的映射关系
|
// 部门和组别的映射关系
|
||||||
const departmentGroups = {
|
const departmentGroups = {
|
||||||
@ -17,6 +18,7 @@ function UserSettingsModal({ show, onClose }) {
|
|||||||
const { user, loading } = useSelector((state) => state.auth);
|
const { user, loading } = useSelector((state) => state.auth);
|
||||||
const [lastPasswordChange] = useState('30天前'); // This would come from backend in real app
|
const [lastPasswordChange] = useState('30天前'); // This would come from backend in real app
|
||||||
const [formData, setFormData] = useState({});
|
const [formData, setFormData] = useState({});
|
||||||
|
const [showChangePassword, setShowChangePassword] = useState(false);
|
||||||
// 可选的组别列表
|
// 可选的组别列表
|
||||||
const [availableGroups, setAvailableGroups] = useState([]);
|
const [availableGroups, setAvailableGroups] = useState([]);
|
||||||
|
|
||||||
@ -198,6 +200,28 @@ function UserSettingsModal({ show, onClose }) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className='mb-4'>
|
||||||
|
<h6 className='text-secondary mb-3'>安全设置</h6>
|
||||||
|
<div className='d-flex justify-content-between align-items-center p-3 bg-light rounded'>
|
||||||
|
<div>
|
||||||
|
<div className='d-flex align-items-center gap-2'>
|
||||||
|
<i className='bi bi-key'></i>
|
||||||
|
<span>修改密码</span>
|
||||||
|
</div>
|
||||||
|
<small className='text-secondary'>上次修改:{lastPasswordChange}</small>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className='btn btn-outline-dark btn-sm'
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setShowChangePassword(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
修改
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className='d-none mb-4'>
|
<div className='d-none mb-4'>
|
||||||
<h6 className='text-secondary mb-3'>安全设置</h6>
|
<h6 className='text-secondary mb-3'>安全设置</h6>
|
||||||
<div className='d-flex justify-content-between align-items-center p-3 bg-light rounded'>
|
<div className='d-flex justify-content-between align-items-center p-3 bg-light rounded'>
|
||||||
@ -260,6 +284,9 @@ function UserSettingsModal({ show, onClose }) {
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
{showChangePassword && (
|
||||||
|
<ChangePasswordModal show={showChangePassword} onClose={() => setShowChangePassword(false)} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import { logoutThunk } from '../store/auth/auth.thunk';
|
|||||||
import UserSettingsModal from '../components/UserSettingsModal';
|
import UserSettingsModal from '../components/UserSettingsModal';
|
||||||
import NotificationCenter from '../components/NotificationCenter';
|
import NotificationCenter from '../components/NotificationCenter';
|
||||||
import SvgIcon from '../components/SvgIcon';
|
import SvgIcon from '../components/SvgIcon';
|
||||||
|
import ChangePasswordModal from '../components/ChangePasswordModal';
|
||||||
import { fetchNotifications } from '../store/notificationCenter/notificationCenter.thunks';
|
import { fetchNotifications } from '../store/notificationCenter/notificationCenter.thunks';
|
||||||
|
|
||||||
export default function HeaderWithNav() {
|
export default function HeaderWithNav() {
|
||||||
@ -14,6 +15,7 @@ export default function HeaderWithNav() {
|
|||||||
const { user } = useSelector((state) => state.auth);
|
const { user } = useSelector((state) => state.auth);
|
||||||
const [showSettings, setShowSettings] = useState(false);
|
const [showSettings, setShowSettings] = useState(false);
|
||||||
const [showNotifications, setShowNotifications] = useState(false);
|
const [showNotifications, setShowNotifications] = useState(false);
|
||||||
|
const [showChangePassword, setShowChangePassword] = useState(false);
|
||||||
const { notifications, unreadCount, isConnected } = useSelector((state) => state.notificationCenter);
|
const { notifications, unreadCount, isConnected } = useSelector((state) => state.notificationCenter);
|
||||||
|
|
||||||
const handleLogout = async () => {
|
const handleLogout = async () => {
|
||||||
@ -135,6 +137,15 @@ export default function HeaderWithNav() {
|
|||||||
个人设置
|
个人设置
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<Link
|
||||||
|
className='dropdown-item'
|
||||||
|
to='#'
|
||||||
|
onClick={() => setShowChangePassword(true)}
|
||||||
|
>
|
||||||
|
修改密码
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
<li className='d-none'>
|
<li className='d-none'>
|
||||||
<hr className='dropdown-divider' />
|
<hr className='dropdown-divider' />
|
||||||
</li>
|
</li>
|
||||||
@ -168,6 +179,7 @@ export default function HeaderWithNav() {
|
|||||||
</nav>
|
</nav>
|
||||||
<UserSettingsModal show={showSettings} onClose={() => setShowSettings(false)} />
|
<UserSettingsModal show={showSettings} onClose={() => setShowSettings(false)} />
|
||||||
<NotificationCenter show={showNotifications} onClose={() => setShowNotifications(false)} />
|
<NotificationCenter show={showNotifications} onClose={() => setShowNotifications(false)} />
|
||||||
|
<ChangePasswordModal show={showChangePassword} onClose={() => setShowChangePassword(false)} />
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -159,11 +159,6 @@ const put = async (url, data) => {
|
|||||||
// Handle DELETE requests with fallback to mock API
|
// Handle DELETE requests with fallback to mock API
|
||||||
const del = async (url) => {
|
const del = async (url) => {
|
||||||
try {
|
try {
|
||||||
if (isServerDown) {
|
|
||||||
console.log(`[MOCK MODE] DELETE ${url}`);
|
|
||||||
return await mockDelete(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await api.delete(url);
|
const res = await api.delete(url);
|
||||||
return res.data;
|
return res.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -217,36 +212,6 @@ export const switchToRealApi = async () => {
|
|||||||
// Handle streaming requests
|
// Handle streaming requests
|
||||||
const streamRequest = async (url, data, onChunk, onError) => {
|
const streamRequest = async (url, data, onChunk, onError) => {
|
||||||
try {
|
try {
|
||||||
if (isServerDown) {
|
|
||||||
console.log(`[MOCK MODE] STREAM ${url}`);
|
|
||||||
// 模拟流式响应
|
|
||||||
setTimeout(
|
|
||||||
() =>
|
|
||||||
onChunk(
|
|
||||||
'{"code":200,"message":"partial","data":{"content":"这是模拟的","conversation_id":"mock-1234"}}'
|
|
||||||
),
|
|
||||||
300
|
|
||||||
);
|
|
||||||
setTimeout(
|
|
||||||
() =>
|
|
||||||
onChunk('{"code":200,"message":"partial","data":{"content":"流式","conversation_id":"mock-1234"}}'),
|
|
||||||
600
|
|
||||||
);
|
|
||||||
setTimeout(
|
|
||||||
() =>
|
|
||||||
onChunk('{"code":200,"message":"partial","data":{"content":"响应","conversation_id":"mock-1234"}}'),
|
|
||||||
900
|
|
||||||
);
|
|
||||||
setTimeout(
|
|
||||||
() =>
|
|
||||||
onChunk(
|
|
||||||
'{"code":200,"message":"partial","data":{"content":"数据","conversation_id":"mock-1234","is_end":true}}'
|
|
||||||
),
|
|
||||||
1200
|
|
||||||
);
|
|
||||||
return { success: true, conversation_id: 'mock-1234' };
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取认证Token
|
// 获取认证Token
|
||||||
const encryptedToken = sessionStorage.getItem('token') || '';
|
const encryptedToken = sessionStorage.getItem('token') || '';
|
||||||
let token = '';
|
let token = '';
|
||||||
@ -259,7 +224,6 @@ const streamRequest = async (url, data, onChunk, onError) => {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
Authorization: token ? `Token ${token}` : '',
|
|
||||||
},
|
},
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
});
|
});
|
||||||
|
@ -819,6 +819,32 @@ export const mockPost = async (url, data, isMultipart = false) => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 验证Token
|
||||||
|
if (url === '/auth/verify-token/') {
|
||||||
|
// 在实际应用中,这里会验证请求头中的Token
|
||||||
|
// 由于是mock API,我们假设所有token都是有效的
|
||||||
|
// 并返回第一个用户作为当前用户
|
||||||
|
|
||||||
|
const user = mockUsers[0];
|
||||||
|
|
||||||
|
return {
|
||||||
|
code: 200,
|
||||||
|
message: 'Token验证成功',
|
||||||
|
data: {
|
||||||
|
user: {
|
||||||
|
id: user.id,
|
||||||
|
username: user.username,
|
||||||
|
email: user.email,
|
||||||
|
name: user.name,
|
||||||
|
department: user.department,
|
||||||
|
group: user.group,
|
||||||
|
role: user.role,
|
||||||
|
avatar: user.avatar,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Create knowledge base
|
// Create knowledge base
|
||||||
if (url === '/knowledge-bases/') {
|
if (url === '/knowledge-bases/') {
|
||||||
const newKnowledgeBase = {
|
const newKnowledgeBase = {
|
||||||
@ -978,6 +1004,49 @@ export const mockPost = async (url, data, isMultipart = false) => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 修改密码
|
||||||
|
if (url === '/auth/change-password/') {
|
||||||
|
const { old_password, new_password } = data;
|
||||||
|
|
||||||
|
// 从请求头中获取token,找到当前用户
|
||||||
|
const token = 'mock-token'; // 在实际情况下,会从请求头中获取并验证
|
||||||
|
const currentUser = mockUsers.find(user => user.id === 'user-001'); // 假设当前用户是第一个用户
|
||||||
|
|
||||||
|
// 验证旧密码
|
||||||
|
if (!currentUser || currentUser.password !== old_password) {
|
||||||
|
throw {
|
||||||
|
response: {
|
||||||
|
status: 400,
|
||||||
|
data: {
|
||||||
|
code: 400,
|
||||||
|
message: '原密码不正确',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新密码
|
||||||
|
const userIndex = mockUsers.findIndex(user => user.id === currentUser.id);
|
||||||
|
if (userIndex !== -1) {
|
||||||
|
mockUsers[userIndex].password = new_password;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
code: 200,
|
||||||
|
message: '密码修改成功',
|
||||||
|
data: { success: true }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户登出
|
||||||
|
if (url === '/auth/logout/') {
|
||||||
|
return {
|
||||||
|
code: 200,
|
||||||
|
message: '登出成功',
|
||||||
|
data: { success: true }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// 上传知识库文档
|
// 上传知识库文档
|
||||||
if (url.match(/\/knowledge-bases\/([^/]+)\/upload_document\//)) {
|
if (url.match(/\/knowledge-bases\/([^/]+)\/upload_document\//)) {
|
||||||
const knowledge_base_id = url.match(/\/knowledge-bases\/([^/]+)\/upload_document\//)[1];
|
const knowledge_base_id = url.match(/\/knowledge-bases\/([^/]+)\/upload_document\//)[1];
|
||||||
@ -1001,31 +1070,31 @@ export const mockPost = async (url, data, isMultipart = false) => {
|
|||||||
// Mark all notifications as read
|
// Mark all notifications as read
|
||||||
if (url === '/notifications/mark-all-as-read/') {
|
if (url === '/notifications/mark-all-as-read/') {
|
||||||
// Update all notifications to be read
|
// Update all notifications to be read
|
||||||
notifications.forEach(notification => {
|
notifications.forEach((notification) => {
|
||||||
notification.is_read = true;
|
notification.is_read = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
code: 200,
|
code: 200,
|
||||||
message: 'All notifications marked as read successfully',
|
message: 'All notifications marked as read successfully',
|
||||||
data: { success: true }
|
data: { success: true },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark a notification as read
|
// Mark a notification as read
|
||||||
if (url.match(/\/notifications\/([^\/]+)\/mark-as-read\//)) {
|
if (url.match(/\/notifications\/([^\/]+)\/mark-as-read\//)) {
|
||||||
const notificationId = url.match(/\/notifications\/([^\/]+)\/mark-as-read\//)[1];
|
const notificationId = url.match(/\/notifications\/([^\/]+)\/mark-as-read\//)[1];
|
||||||
const notificationIndex = notifications.findIndex(n => n.id === notificationId);
|
const notificationIndex = notifications.findIndex((n) => n.id === notificationId);
|
||||||
|
|
||||||
if (notificationIndex !== -1) {
|
if (notificationIndex !== -1) {
|
||||||
notifications[notificationIndex] = {
|
notifications[notificationIndex] = {
|
||||||
...notifications[notificationIndex],
|
...notifications[notificationIndex],
|
||||||
is_read: true
|
is_read: true,
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
code: 200,
|
code: 200,
|
||||||
message: 'Notification marked as read successfully',
|
message: 'Notification marked as read successfully',
|
||||||
data: { success: true, notification: notifications[notificationIndex] }
|
data: { success: true, notification: notifications[notificationIndex] },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ export const initWebSocket = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const wsUrl = `${WS_BASE_URL}/ws/notifications/?token=${token}`;
|
const wsUrl = `${WS_BASE_URL}/ws/notifications?token=${token}`;
|
||||||
console.log('正在连接WebSocket...', wsUrl.substring(0, wsUrl.indexOf('?')));
|
console.log('正在连接WebSocket...', wsUrl.substring(0, wsUrl.indexOf('?')));
|
||||||
|
|
||||||
socket = new WebSocket(wsUrl);
|
socket = new WebSocket(wsUrl);
|
||||||
|
@ -137,3 +137,43 @@ export const updateProfileThunk = createAsyncThunk('auth/updateProfile', async (
|
|||||||
return rejectWithValue(errorMessage);
|
return rejectWithValue(errorMessage);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 修改密码
|
||||||
|
export const changePasswordThunk = createAsyncThunk(
|
||||||
|
'auth/changePassword',
|
||||||
|
async ({ old_password, new_password }, { rejectWithValue, dispatch }) => {
|
||||||
|
try {
|
||||||
|
const { message, code } = await post('/auth/change-password/', {
|
||||||
|
old_password,
|
||||||
|
new_password
|
||||||
|
});
|
||||||
|
|
||||||
|
if (code !== 200) {
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示密码修改成功通知
|
||||||
|
dispatch(
|
||||||
|
showNotification({
|
||||||
|
message: '密码修改成功,请重新登录',
|
||||||
|
type: 'success',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// 修改成功后清除登录状态
|
||||||
|
sessionStorage.removeItem('token');
|
||||||
|
dispatch(logout());
|
||||||
|
|
||||||
|
return { success: true };
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage = error.response?.data?.message || '密码修改失败,请稍后重试';
|
||||||
|
dispatch(
|
||||||
|
showNotification({
|
||||||
|
message: errorMessage,
|
||||||
|
type: 'danger',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return rejectWithValue(errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
Loading…
Reference in New Issue
Block a user