Compare commits
2 Commits
d84fd8934e
...
79ee33be7c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79ee33be7c | ||
|
|
83df8a7373 |
@@ -120,7 +120,7 @@ const DialogComponent: React.FC<DialogComponentProps> = ({ dialog, onClose }) =>
|
|||||||
<Box sx={{ display: 'flex', alignItems: 'center', flex: 1 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', flex: 1 }}>
|
||||||
{getDialogIcon()}
|
{getDialogIcon()}
|
||||||
<Typography variant="h6" component="span">
|
<Typography variant="h6" component="span">
|
||||||
{config.title || t('dialog.defaultTitle')}
|
{config.title || t('dialog.confirm')}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
|||||||
@@ -394,12 +394,12 @@ export const useLoginForm = () => {
|
|||||||
password: {
|
password: {
|
||||||
required: t('login.passwordPlaceholder'),
|
required: t('login.passwordPlaceholder'),
|
||||||
minLength: {
|
minLength: {
|
||||||
value: 8,
|
value: 6,
|
||||||
message: t('setting.passwordMinLength')
|
message: t('setting.passwordMinLength')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
confirmPassword: {
|
confirmPassword: {
|
||||||
required: t('confirmPasswordRequired'),
|
required: t('login.confirmPasswordRequired'),
|
||||||
validate: (value: string) => {
|
validate: (value: string) => {
|
||||||
const pwd = registerForm.getValues('password');
|
const pwd = registerForm.getValues('password');
|
||||||
return value === pwd || t('setting.passwordMismatch');
|
return value === pwd || t('setting.passwordMismatch');
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ export function useProfileSetting() {
|
|||||||
password: oldPassword,
|
password: oldPassword,
|
||||||
new_password: newPassword,
|
new_password: newPassword,
|
||||||
});
|
});
|
||||||
|
return res;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -262,6 +262,11 @@ export default {
|
|||||||
emailInvalid: 'Invalid email address',
|
emailInvalid: 'Invalid email address',
|
||||||
passwordLabel: 'Password',
|
passwordLabel: 'Password',
|
||||||
passwordPlaceholder: 'Please input password',
|
passwordPlaceholder: 'Please input password',
|
||||||
|
confirmPasswordRequired: 'Please confirm your password',
|
||||||
|
confirmPassword: 'Confirm password',
|
||||||
|
confirmPasswordMessage: 'Please confirm your password!',
|
||||||
|
confirmPasswordNonMatchMessage:
|
||||||
|
'The confirm password that you entered do not match!',
|
||||||
rememberMe: 'Remember me',
|
rememberMe: 'Remember me',
|
||||||
signInTip: 'Don’t have an account?',
|
signInTip: 'Don’t have an account?',
|
||||||
signUpTip: 'Already have an account?',
|
signUpTip: 'Already have an account?',
|
||||||
@@ -942,7 +947,7 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s
|
|||||||
serverTypeRequired: 'Please select server type',
|
serverTypeRequired: 'Please select server type',
|
||||||
testConnection: 'Test Connection',
|
testConnection: 'Test Connection',
|
||||||
testing: 'Testing...',
|
testing: 'Testing...',
|
||||||
connectionSuccess: 'Connection successful! Found {count} tools',
|
connectionSuccess: 'Connection successful! Found {{count}} tools',
|
||||||
availableTools: 'Available Tools',
|
availableTools: 'Available Tools',
|
||||||
connectionFailed: 'Connection failed',
|
connectionFailed: 'Connection failed',
|
||||||
testFailed: 'Test failed',
|
testFailed: 'Test failed',
|
||||||
|
|||||||
@@ -244,6 +244,11 @@ export default {
|
|||||||
emailInvalid: '无效的邮箱地址',
|
emailInvalid: '无效的邮箱地址',
|
||||||
passwordLabel: '密码',
|
passwordLabel: '密码',
|
||||||
passwordPlaceholder: '请输入密码',
|
passwordPlaceholder: '请输入密码',
|
||||||
|
confirmPasswordRequired: '请确认您的密码',
|
||||||
|
confirmPassword: '确认密码',
|
||||||
|
confirmPasswordMessage: '请确认您的密码!',
|
||||||
|
confirmPasswordNonMatchMessage:
|
||||||
|
'确认密码与密码不匹配!',
|
||||||
rememberMe: '记住我',
|
rememberMe: '记住我',
|
||||||
signInTip: '没有帐户?',
|
signInTip: '没有帐户?',
|
||||||
signUpTip: '已经有帐户?',
|
signUpTip: '已经有帐户?',
|
||||||
@@ -941,7 +946,7 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于
|
|||||||
serverTypeRequired: '请选择服务器类型',
|
serverTypeRequired: '请选择服务器类型',
|
||||||
testConnection: '测试连接',
|
testConnection: '测试连接',
|
||||||
testing: '测试中...',
|
testing: '测试中...',
|
||||||
connectionSuccess: '连接成功!发现 {count} 个工具',
|
connectionSuccess: '连接成功!发现 {{count}} 个工具',
|
||||||
availableTools: '可用工具',
|
availableTools: '可用工具',
|
||||||
connectionFailed: '连接失败',
|
connectionFailed: '连接失败',
|
||||||
testFailed: '测试失败',
|
testFailed: '测试失败',
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ import {
|
|||||||
BugReportOutlined as ProcessIcon,
|
BugReportOutlined as ProcessIcon,
|
||||||
Download as DownloadIcon,
|
Download as DownloadIcon,
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
import { DataGrid, type GridColDef, type GridRowSelectionModel } from '@mui/x-data-grid';
|
import { DataGrid, type GridColDef, type GridRowSelectionModel, type GridRowId } from '@mui/x-data-grid';
|
||||||
import { zhCN, enUS } from '@mui/x-data-grid/locales';
|
import { zhCN, enUS } from '@mui/x-data-grid/locales';
|
||||||
import type { IKnowledgeFile } from '@/interfaces/database/knowledge';
|
import type { IKnowledgeFile } from '@/interfaces/database/knowledge';
|
||||||
import type { IDocumentInfoFilter } from '@/interfaces/database/document';
|
import type { IDocumentInfoFilter } from '@/interfaces/database/document';
|
||||||
@@ -163,7 +163,7 @@ const getRunStatusChip = (run: RunningStatus, progress: number) => {
|
|||||||
<CircularProgress size={16} />
|
<CircularProgress size={16} />
|
||||||
<Box sx={{ minWidth: 80 }}>
|
<Box sx={{ minWidth: 80 }}>
|
||||||
<Typography variant="caption" color="text.secondary">
|
<Typography variant="caption" color="text.secondary">
|
||||||
{(progress*100).toFixed(2)}%
|
{(progress * 100).toFixed(2)}%
|
||||||
</Typography>
|
</Typography>
|
||||||
<LinearProgress variant="determinate" value={progress} sx={{ height: 4, borderRadius: 2 }} />
|
<LinearProgress variant="determinate" value={progress} sx={{ height: 4, borderRadius: 2 }} />
|
||||||
</Box>
|
</Box>
|
||||||
@@ -218,10 +218,10 @@ const DocumentListComponent: React.FC<DocumentListComponentProps> = ({
|
|||||||
const { i18n, t } = useTranslation();
|
const { i18n, t } = useTranslation();
|
||||||
|
|
||||||
// 根据当前语言获取DataGrid的localeText
|
// 根据当前语言获取DataGrid的localeText
|
||||||
const getDataGridLocale = () => {
|
const getDataGridLocale = useCallback(() => {
|
||||||
const currentLanguage = i18n.language;
|
const currentLanguage = i18n.language;
|
||||||
return currentLanguage === LanguageAbbreviation.Zh ? zhCN : enUS;
|
return currentLanguage === LanguageAbbreviation.Zh ? zhCN : enUS;
|
||||||
};
|
}, [i18n]);
|
||||||
|
|
||||||
const handleFilterSubmit = useCallback(() => {
|
const handleFilterSubmit = useCallback(() => {
|
||||||
const filter = {
|
const filter = {
|
||||||
@@ -346,6 +346,24 @@ const DocumentListComponent: React.FC<DocumentListComponentProps> = ({
|
|||||||
setSelectedSuffix(typeof value === 'string' ? value.split(',') : value);
|
setSelectedSuffix(typeof value === 'string' ? value.split(',') : value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 选中数量计算(强类型)
|
||||||
|
const countSelected = (model: GridRowSelectionModel, totalCount: number): number => {
|
||||||
|
const size = model.ids.size ?? 0;
|
||||||
|
return model.type === 'exclude' ? Math.max(totalCount - size, 0) : size;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取当前页有效选中 ID 列表(强类型)
|
||||||
|
const getSelectedIdsOnCurrentPage = (model: GridRowSelectionModel, currentFiles: IKnowledgeFile[]): string[] => {
|
||||||
|
if (model.type === 'include') {
|
||||||
|
return Array.from(model.ids).map((id) => id.toString());
|
||||||
|
}
|
||||||
|
const excluded = model.ids;
|
||||||
|
return currentFiles
|
||||||
|
.map((f) => f.id)
|
||||||
|
.filter((id) => !excluded.has(id))
|
||||||
|
.map((id) => id.toString());
|
||||||
|
};
|
||||||
|
|
||||||
// 处理分页变化
|
// 处理分页变化
|
||||||
const handlePaginationModelChange = (model: { page: number; pageSize: number }) => {
|
const handlePaginationModelChange = (model: { page: number; pageSize: number }) => {
|
||||||
if (model.page !== page - 1) { // DataGrid的page是0-based,我们的是1-based
|
if (model.page !== page - 1) { // DataGrid的page是0-based,我们的是1-based
|
||||||
@@ -603,23 +621,22 @@ const DocumentListComponent: React.FC<DocumentListComponentProps> = ({
|
|||||||
{t('knowledge.uploadFile')}
|
{t('knowledge.uploadFile')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{rowSelectionModel.ids.size > 0 && (
|
{countSelected(rowSelectionModel, total) > 0 && (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
startIcon={<RefreshIcon />}
|
startIcon={<RefreshIcon />}
|
||||||
onClick={() => onReparse(Array.from(rowSelectionModel.ids).map((id) => id.toString()))}
|
onClick={() => onReparse(getSelectedIdsOnCurrentPage(rowSelectionModel, files))}
|
||||||
>
|
>
|
||||||
{t('knowledge.reparse')} ({rowSelectionModel.ids.size})
|
{t('knowledge.reparse')} ({countSelected(rowSelectionModel, total)})
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
color="error"
|
color="error"
|
||||||
startIcon={<DeleteIcon />}
|
startIcon={<DeleteIcon />}
|
||||||
onClick={() => onDelete(Array.from(rowSelectionModel.ids).map((id) => id.toString()))}
|
onClick={() => onDelete(getSelectedIdsOnCurrentPage(rowSelectionModel, files))}
|
||||||
>
|
>
|
||||||
{t('common.delete')} ({rowSelectionModel.ids.size})
|
{t('common.delete')} ({countSelected(rowSelectionModel, total)})
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@@ -640,7 +657,9 @@ const DocumentListComponent: React.FC<DocumentListComponentProps> = ({
|
|||||||
checkboxSelection
|
checkboxSelection
|
||||||
disableRowSelectionOnClick
|
disableRowSelectionOnClick
|
||||||
rowSelectionModel={rowSelectionModel}
|
rowSelectionModel={rowSelectionModel}
|
||||||
onRowSelectionModelChange={onRowSelectionModelChange}
|
onRowSelectionModelChange={(model: GridRowSelectionModel) => {
|
||||||
|
onRowSelectionModelChange(model);
|
||||||
|
}}
|
||||||
pageSizeOptions={[10, 25, 50, 100]}
|
pageSizeOptions={[10, 25, 50, 100]}
|
||||||
paginationMode="server"
|
paginationMode="server"
|
||||||
rowCount={total}
|
rowCount={total}
|
||||||
|
|||||||
@@ -162,20 +162,20 @@ function TestChunkResult(props: TestChunkResultProps) {
|
|||||||
</Typography>
|
</Typography>
|
||||||
<Stack direction="row" spacing={1}>
|
<Stack direction="row" spacing={1}>
|
||||||
<Chip
|
<Chip
|
||||||
label={t('knowledge.similarity', { value: (chunk.similarity * 100).toFixed(1) })}
|
label={`${t('knowledge.similarity')}: ${(chunk.similarity).toFixed(0)}`}
|
||||||
size="small"
|
size="small"
|
||||||
color="primary"
|
color="primary"
|
||||||
/>
|
/>
|
||||||
{chunk.vector_similarity !== undefined && (
|
{chunk.vector_similarity !== undefined && (
|
||||||
<Chip
|
<Chip
|
||||||
label={t('knowledge.vectorSimilarity', { value: (chunk.vector_similarity * 100).toFixed(1) })}
|
label={`${t('knowledge.vectorSimilarity')}: ${(chunk.vector_similarity).toFixed(2)}`}
|
||||||
size="small"
|
size="small"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{chunk.term_similarity !== undefined && (
|
{chunk.term_similarity !== undefined && (
|
||||||
<Chip
|
<Chip
|
||||||
label={t('knowledge.termSimilarity', { value: (chunk.term_similarity * 100).toFixed(1) })}
|
label={`${t('knowledge.termSimilarity')}: ${(chunk.term_similarity).toFixed(0)}`}
|
||||||
size="small"
|
size="small"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ function KnowledgeBaseDetail() {
|
|||||||
// 删除文件
|
// 删除文件
|
||||||
const handleDeleteFiles = async () => {
|
const handleDeleteFiles = async () => {
|
||||||
try {
|
try {
|
||||||
await deleteDocuments(Array.from(rowSelectionModel.ids) as string[]);
|
await deleteDocuments(Array.from(rowSelectionModel.ids).map((id) => id.toString()));
|
||||||
setDeleteDialogOpen(false);
|
setDeleteDialogOpen(false);
|
||||||
setRowSelectionModel({
|
setRowSelectionModel({
|
||||||
type: 'include',
|
type: 'include',
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { Box, Grid, Paper, Typography, IconButton, TextField, Tabs, Tab, Fab, Avatar, Chip, Dialog, DialogTitle, DialogContent, DialogActions, Button, Card, CardContent, Divider } from '@mui/material';
|
import { Box, Grid, Paper, Typography, IconButton, TextField, Tabs, Tab, Fab, Avatar, Chip, Dialog, DialogTitle, DialogContent, DialogActions, Button, Card, CardContent, Divider } from '@mui/material';
|
||||||
import { DataGrid, type GridColDef } from '@mui/x-data-grid';
|
import { DataGrid, type GridColDef } from '@mui/x-data-grid';
|
||||||
import {
|
import {
|
||||||
@@ -10,7 +10,6 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
import { useKnowledgeOverview, useKnowledgeDetail } from '@/hooks/knowledge-hooks';
|
import { useKnowledgeOverview, useKnowledgeDetail } from '@/hooks/knowledge-hooks';
|
||||||
import logger from '@/utils/logger';
|
import logger from '@/utils/logger';
|
||||||
import i18n from '@/locales';
|
|
||||||
import { enUS, zhCN } from '@mui/x-data-grid/locales';
|
import { enUS, zhCN } from '@mui/x-data-grid/locales';
|
||||||
import KnowledgeBreadcrumbs from './components/KnowledgeBreadcrumbs';
|
import KnowledgeBreadcrumbs from './components/KnowledgeBreadcrumbs';
|
||||||
import { LanguageAbbreviation } from '@/constants/common';
|
import { LanguageAbbreviation } from '@/constants/common';
|
||||||
@@ -38,14 +37,14 @@ interface KnowledgeLogsPageProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function KnowledgeLogsPage({ embedded = false, kbId: kbIdProp }: KnowledgeLogsPageProps) {
|
function KnowledgeLogsPage({ embedded = false, kbId: kbIdProp }: KnowledgeLogsPageProps) {
|
||||||
const { t } = useTranslation();
|
const { t, i18n } = useTranslation();
|
||||||
const [paginationModel, setPaginationModel] = React.useState({ page: 0, pageSize: 50 });
|
const [paginationModel, setPaginationModel] = React.useState({ page: 0, pageSize: 50 });
|
||||||
|
|
||||||
// 根据当前语言获取DataGrid的localeText
|
// 根据当前语言获取DataGrid的localeText
|
||||||
const getDataGridLocale = () => {
|
const getDataGridLocale = useCallback(() => {
|
||||||
const currentLanguage = i18n.language;
|
const currentLanguage = i18n.language;
|
||||||
return currentLanguage === LanguageAbbreviation.Zh ? zhCN : enUS;
|
return currentLanguage === LanguageAbbreviation.Zh ? zhCN : enUS;
|
||||||
};
|
}, [i18n]);
|
||||||
|
|
||||||
// 路由参数与数据Hook
|
// 路由参数与数据Hook
|
||||||
const { id: kbIdParam = '' } = useParams();
|
const { id: kbIdParam = '' } = useParams();
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ const Login = () => {
|
|||||||
fullWidth
|
fullWidth
|
||||||
id="register-confirm-password"
|
id="register-confirm-password"
|
||||||
type={showConfirmPassword ? 'text' : 'password'}
|
type={showConfirmPassword ? 'text' : 'password'}
|
||||||
placeholder={t('confirmPassword')}
|
placeholder={t('login.confirmPassword')}
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
{...registerForm.register('confirmPassword', registerValidation.confirmPassword)}
|
{...registerForm.register('confirmPassword', registerValidation.confirmPassword)}
|
||||||
error={!!registerForm.formState.errors.confirmPassword}
|
error={!!registerForm.formState.errors.confirmPassword}
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ import logger from '@/utils/logger';
|
|||||||
interface ChangePasswordDialogProps {
|
interface ChangePasswordDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
changeUserPassword: (data: { password: string; new_password: string }) => Promise<void>;
|
onSuccess: () => void;
|
||||||
|
changeUserPassword: (data: { password: string; new_password: string }) => Promise<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PasswordFormData {
|
interface PasswordFormData {
|
||||||
@@ -32,7 +33,7 @@ interface PasswordFormData {
|
|||||||
/**
|
/**
|
||||||
* 修改密码对话框
|
* 修改密码对话框
|
||||||
*/
|
*/
|
||||||
function ChangePasswordDialog({ open, onClose, changeUserPassword }: ChangePasswordDialogProps) {
|
function ChangePasswordDialog({ open, onClose, onSuccess, changeUserPassword }: ChangePasswordDialogProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { showMessage } = useSnackbar();
|
const { showMessage } = useSnackbar();
|
||||||
|
|
||||||
@@ -133,20 +134,19 @@ function ChangePasswordDialog({ open, onClose, changeUserPassword }: ChangePassw
|
|||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
await changeUserPassword({
|
const res = await changeUserPassword({
|
||||||
password: formData.currentPassword,
|
password: formData.currentPassword,
|
||||||
new_password: formData.newPassword
|
new_password: formData.newPassword
|
||||||
});
|
});
|
||||||
|
|
||||||
|
logger.info('修改密码成功:', res);
|
||||||
|
|
||||||
showMessage.success(t('setting.passwordChangeSuccess'));
|
showMessage.success(t('setting.passwordChangeSuccess'));
|
||||||
handleClose();
|
handleClose();
|
||||||
|
// delay 1000 ms
|
||||||
|
setTimeout(() => onSuccess(), 1000);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
logger.error('修改密码失败:', error);
|
logger.error('修改密码失败:', error);
|
||||||
if (error.response?.status === 400) {
|
|
||||||
showMessage.error(t('setting.currentPasswordIncorrect'));
|
|
||||||
} else {
|
|
||||||
showMessage.error(t('setting.passwordChangeError'));
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
@@ -217,7 +217,7 @@ function ChangePasswordDialog({ open, onClose, changeUserPassword }: ChangePassw
|
|||||||
value={formData.newPassword}
|
value={formData.newPassword}
|
||||||
onChange={handleInputChange('newPassword')}
|
onChange={handleInputChange('newPassword')}
|
||||||
error={!!errors.newPassword}
|
error={!!errors.newPassword}
|
||||||
helperText={errors.newPassword || '密码长度至少6位'}
|
helperText={errors.newPassword}
|
||||||
InputProps={{
|
InputProps={{
|
||||||
endAdornment: (
|
endAdornment: (
|
||||||
<InputAdornment position="end">
|
<InputAdornment position="end">
|
||||||
|
|||||||
@@ -7,11 +7,14 @@ import ChangePasswordDialog from "./components/ChangePasswordDialog";
|
|||||||
import { useProfileSetting } from "@/hooks/setting-hooks";
|
import { useProfileSetting } from "@/hooks/setting-hooks";
|
||||||
import logger from "@/utils/logger";
|
import logger from "@/utils/logger";
|
||||||
import { useUserData } from "@/hooks/useUserData";
|
import { useUserData } from "@/hooks/useUserData";
|
||||||
|
import { useAuth } from '@/hooks/login-hooks';
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
function ProfileSetting() {
|
function ProfileSetting() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { userInfo, updateUserInfo: updateUserInfoFunc, changeUserPassword: changeUserPasswordFunc } = useProfileSetting();
|
const { userInfo, updateUserInfo: updateUserInfoFunc, changeUserPassword: changeUserPasswordFunc } = useProfileSetting();
|
||||||
const { refreshUserData } = useUserData();
|
const { refreshUserData } = useUserData();
|
||||||
|
const { logout } = useAuth();
|
||||||
const [passwordDialogOpen, setPasswordDialogOpen] = useState(false);
|
const [passwordDialogOpen, setPasswordDialogOpen] = useState(false);
|
||||||
|
|
||||||
logger.debug('userInfo', userInfo);
|
logger.debug('userInfo', userInfo);
|
||||||
@@ -66,6 +69,10 @@ function ProfileSetting() {
|
|||||||
<ChangePasswordDialog
|
<ChangePasswordDialog
|
||||||
open={passwordDialogOpen}
|
open={passwordDialogOpen}
|
||||||
onClose={handleClosePasswordDialog}
|
onClose={handleClosePasswordDialog}
|
||||||
|
onSuccess={() => {
|
||||||
|
// 使用 useAuth 的 logout,确保清除本地存储令牌并跳转登录
|
||||||
|
logout();
|
||||||
|
}}
|
||||||
changeUserPassword={changeUserPasswordFunc}
|
changeUserPassword={changeUserPasswordFunc}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import {
|
|||||||
ExitToApp as ExitToAppIcon
|
ExitToApp as ExitToAppIcon
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
import { useTeamSetting } from '@/hooks/setting-hooks';
|
import { useTeamSetting } from '@/hooks/setting-hooks';
|
||||||
|
import { useDialog } from '@/hooks/useDialog';
|
||||||
|
|
||||||
interface TenantUser {
|
interface TenantUser {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -88,8 +89,15 @@ function TeamsSetting() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const dialog = useDialog();
|
||||||
|
|
||||||
const handleDeleteUser = async (userId: string) => {
|
const handleDeleteUser = async (userId: string) => {
|
||||||
await deleteUser(userId);
|
dialog.confirm({
|
||||||
|
content: t('setting.sureDelete'),
|
||||||
|
onConfirm: async () => {
|
||||||
|
await deleteUser(userId);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAgreeTenant = async (tenantId: string) => {
|
const handleAgreeTenant = async (tenantId: string) => {
|
||||||
@@ -170,7 +178,7 @@ function TeamsSetting() {
|
|||||||
<IconButton
|
<IconButton
|
||||||
size="small"
|
size="small"
|
||||||
color="error"
|
color="error"
|
||||||
onClick={() => handleDeleteUser(user.id)}
|
onClick={() => handleDeleteUser(user.user_id)}
|
||||||
>
|
>
|
||||||
<DeleteIcon />
|
<DeleteIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|||||||
@@ -184,17 +184,29 @@ request.interceptors.response.use(
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { status, data } = error.response || {};
|
const { status, data } = error.response || {};
|
||||||
if (status == 401) {
|
if (status == 401 || status == 400) {
|
||||||
logger.info('401', data)
|
logger.info('401 || 400', data)
|
||||||
const detail = data['detail']
|
const detail = data['detail']
|
||||||
if (detail) {
|
if (detail) {
|
||||||
const error = new CustomError(detail || i18n.t('message.requestError'));
|
const error = new CustomError(detail || i18n.t('message.requestError'));
|
||||||
if (detail.includes('not registered')) {
|
if (status == 401) {
|
||||||
|
const arr = ['not registered', 'Password error', 'Email and password do not match']
|
||||||
|
const redirectArr = ['Invalid or expired token', 'No Authorization']
|
||||||
|
if (arr.some(item => detail.includes(item))) {
|
||||||
|
error.code = data?.code || -1;
|
||||||
|
error.response = data;
|
||||||
|
snackbar.error(detail);
|
||||||
|
} else if (redirectArr.some(item => detail.includes(item))) {
|
||||||
|
redirectToLogin();
|
||||||
|
} else {
|
||||||
|
error.code = data?.code || -1;
|
||||||
|
error.response = data;
|
||||||
|
snackbar.error(detail);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
error.code = data?.code || -1;
|
error.code = data?.code || -1;
|
||||||
error.response = data;
|
error.response = data;
|
||||||
snackbar.error(detail);
|
snackbar.error(detail);
|
||||||
} else {
|
|
||||||
redirectToLogin();
|
|
||||||
}
|
}
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user