feat: 账号凭据默认脱敏并支持主动查看
This commit is contained in:
@@ -5,7 +5,7 @@ import {
|
||||
} from 'antd';
|
||||
import { message } from '../utils/antdMessage';
|
||||
import type { TableProps } from 'antd';
|
||||
import { ImportOutlined, DeleteOutlined, TagOutlined, FilterOutlined } from '@ant-design/icons';
|
||||
import { ImportOutlined, DeleteOutlined, TagOutlined, FilterOutlined, EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons';
|
||||
import { accountApi, userApi, type AccountItem, type BasicSummary, type UserInfo } from '../api/modules';
|
||||
import { usePermissions } from '../hooks/usePermissions';
|
||||
import { formatTime } from '../utils/time';
|
||||
@@ -33,6 +33,7 @@ export default function AccountsPage() {
|
||||
const [selectedAllMatching, setSelectedAllMatching] = useState(false);
|
||||
const [batchTagInput, setBatchTagInput] = useState('');
|
||||
const [batchTagVisible, setBatchTagVisible] = useState(false);
|
||||
const [sensitiveVisible, setSensitiveVisible] = useState(false);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [summary, setSummary] = useState<BasicSummary>({ total: 0, assigned_count: 0, unassigned_count: 0, tag_count: 0 });
|
||||
const [pageSize, setPageSize] = useState(() => {
|
||||
@@ -58,7 +59,7 @@ export default function AccountsPage() {
|
||||
page: currentPage,
|
||||
page_size: pageSize,
|
||||
search: searchText.trim() || undefined,
|
||||
include_sensitive: canViewFull,
|
||||
include_sensitive: canViewFull && sensitiveVisible,
|
||||
});
|
||||
setAccounts(data.items);
|
||||
setTotal(data.total);
|
||||
@@ -67,7 +68,7 @@ export default function AccountsPage() {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [canViewFull, currentPage, pageSize, searchText, tagFilter]);
|
||||
}, [canViewFull, currentPage, pageSize, searchText, sensitiveVisible, tagFilter]);
|
||||
|
||||
const loadSummary = useCallback(async () => {
|
||||
try {
|
||||
@@ -224,6 +225,27 @@ export default function AccountsPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleSensitiveVisibility = () => {
|
||||
if (sensitiveVisible) {
|
||||
// 立即清除已加载的敏感内容,再由 effect 重新获取脱敏列表。
|
||||
setAccounts((previous) => previous.map((account) => ({
|
||||
...account,
|
||||
password: undefined,
|
||||
email: undefined,
|
||||
email_password: undefined,
|
||||
})));
|
||||
setSensitiveVisible(false);
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: '显示账号凭据?',
|
||||
content: '密码、邮箱和邮箱密码将临时显示在当前页面。关闭或刷新页面后会恢复脱敏。',
|
||||
okText: '显示',
|
||||
cancelText: '取消',
|
||||
onOk: () => setSensitiveVisible(true),
|
||||
});
|
||||
};
|
||||
|
||||
const columns: TableProps<AccountItem>['columns'] = [
|
||||
{ title: 'ID', dataIndex: 'id', width: 60 },
|
||||
{ title: '用户名', dataIndex: 'username' },
|
||||
@@ -277,8 +299,7 @@ export default function AccountsPage() {
|
||||
},
|
||||
];
|
||||
|
||||
// 只有管理员可看完整字段(密码、邮箱等)
|
||||
if (canViewFull) {
|
||||
if (canViewFull && sensitiveVisible) {
|
||||
columns.push(
|
||||
{ title: '密码', dataIndex: 'password', width: 120 },
|
||||
{ title: '邮箱', dataIndex: 'email' },
|
||||
@@ -346,6 +367,14 @@ export default function AccountsPage() {
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
/>
|
||||
{canViewFull && (
|
||||
<Button
|
||||
icon={sensitiveVisible ? <EyeInvisibleOutlined /> : <EyeOutlined />}
|
||||
onClick={handleSensitiveVisibility}
|
||||
>
|
||||
{sensitiveVisible ? '隐藏凭据' : '显示凭据'}
|
||||
</Button>
|
||||
)}
|
||||
{canImport && (
|
||||
<Button
|
||||
disabled={selectedCount === 0}
|
||||
|
||||
Reference in New Issue
Block a user