feat: add status filtering and bulk enable/disable functionality in multi-key management
This commit is contained in:
@@ -1057,6 +1057,7 @@ type MultiKeyManageRequest struct {
|
|||||||
KeyIndex *int `json:"key_index,omitempty"` // for disable_key and enable_key actions
|
KeyIndex *int `json:"key_index,omitempty"` // for disable_key and enable_key actions
|
||||||
Page int `json:"page,omitempty"` // for get_key_status pagination
|
Page int `json:"page,omitempty"` // for get_key_status pagination
|
||||||
PageSize int `json:"page_size,omitempty"` // for get_key_status pagination
|
PageSize int `json:"page_size,omitempty"` // for get_key_status pagination
|
||||||
|
Status *int `json:"status,omitempty"` // for get_key_status filtering: 1=enabled, 2=manual_disabled, 3=auto_disabled, nil=all
|
||||||
}
|
}
|
||||||
|
|
||||||
// MultiKeyStatusResponse represents the response for key status query
|
// MultiKeyStatusResponse represents the response for key status query
|
||||||
@@ -1109,7 +1110,6 @@ func ManageMultiKeys(c *gin.Context) {
|
|||||||
switch request.Action {
|
switch request.Action {
|
||||||
case "get_key_status":
|
case "get_key_status":
|
||||||
keys := channel.GetKeys()
|
keys := channel.GetKeys()
|
||||||
total := len(keys)
|
|
||||||
|
|
||||||
// Default pagination parameters
|
// Default pagination parameters
|
||||||
page := request.Page
|
page := request.Page
|
||||||
@@ -1121,23 +1121,11 @@ func ManageMultiKeys(c *gin.Context) {
|
|||||||
pageSize = 50 // Default page size
|
pageSize = 50 // Default page size
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate pagination
|
// Statistics for all keys (unchanged by filtering)
|
||||||
totalPages := (total + pageSize - 1) / pageSize
|
|
||||||
if page > totalPages && totalPages > 0 {
|
|
||||||
page = totalPages
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate range
|
|
||||||
start := (page - 1) * pageSize
|
|
||||||
end := start + pageSize
|
|
||||||
if end > total {
|
|
||||||
end = total
|
|
||||||
}
|
|
||||||
|
|
||||||
// Statistics for all keys
|
|
||||||
var enabledCount, manualDisabledCount, autoDisabledCount int
|
var enabledCount, manualDisabledCount, autoDisabledCount int
|
||||||
|
|
||||||
var keyStatusList []KeyStatus
|
// Build all key status data first
|
||||||
|
var allKeyStatusList []KeyStatus
|
||||||
for i, key := range keys {
|
for i, key := range keys {
|
||||||
status := 1 // default enabled
|
status := 1 // default enabled
|
||||||
var disabledTime int64
|
var disabledTime int64
|
||||||
@@ -1149,7 +1137,7 @@ func ManageMultiKeys(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count for statistics
|
// Count for statistics (all keys)
|
||||||
switch status {
|
switch status {
|
||||||
case 1:
|
case 1:
|
||||||
enabledCount++
|
enabledCount++
|
||||||
@@ -1159,45 +1147,77 @@ func ManageMultiKeys(c *gin.Context) {
|
|||||||
autoDisabledCount++
|
autoDisabledCount++
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only include keys in current page
|
if status != 1 {
|
||||||
if i >= start && i < end {
|
if channel.ChannelInfo.MultiKeyDisabledTime != nil {
|
||||||
if status != 1 {
|
disabledTime = channel.ChannelInfo.MultiKeyDisabledTime[i]
|
||||||
if channel.ChannelInfo.MultiKeyDisabledTime != nil {
|
|
||||||
disabledTime = channel.ChannelInfo.MultiKeyDisabledTime[i]
|
|
||||||
}
|
|
||||||
if channel.ChannelInfo.MultiKeyDisabledReason != nil {
|
|
||||||
reason = channel.ChannelInfo.MultiKeyDisabledReason[i]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if channel.ChannelInfo.MultiKeyDisabledReason != nil {
|
||||||
// Create key preview (first 10 chars)
|
reason = channel.ChannelInfo.MultiKeyDisabledReason[i]
|
||||||
keyPreview := key
|
|
||||||
if len(key) > 10 {
|
|
||||||
keyPreview = key[:10] + "..."
|
|
||||||
}
|
}
|
||||||
|
|
||||||
keyStatusList = append(keyStatusList, KeyStatus{
|
|
||||||
Index: i,
|
|
||||||
Status: status,
|
|
||||||
DisabledTime: disabledTime,
|
|
||||||
Reason: reason,
|
|
||||||
KeyPreview: keyPreview,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create key preview (first 10 chars)
|
||||||
|
keyPreview := key
|
||||||
|
if len(key) > 10 {
|
||||||
|
keyPreview = key[:10] + "..."
|
||||||
|
}
|
||||||
|
|
||||||
|
allKeyStatusList = append(allKeyStatusList, KeyStatus{
|
||||||
|
Index: i,
|
||||||
|
Status: status,
|
||||||
|
DisabledTime: disabledTime,
|
||||||
|
Reason: reason,
|
||||||
|
KeyPreview: keyPreview,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply status filter if specified
|
||||||
|
var filteredKeyStatusList []KeyStatus
|
||||||
|
if request.Status != nil {
|
||||||
|
for _, keyStatus := range allKeyStatusList {
|
||||||
|
if keyStatus.Status == *request.Status {
|
||||||
|
filteredKeyStatusList = append(filteredKeyStatusList, keyStatus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
filteredKeyStatusList = allKeyStatusList
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate pagination based on filtered results
|
||||||
|
filteredTotal := len(filteredKeyStatusList)
|
||||||
|
totalPages := (filteredTotal + pageSize - 1) / pageSize
|
||||||
|
if totalPages == 0 {
|
||||||
|
totalPages = 1
|
||||||
|
}
|
||||||
|
if page > totalPages {
|
||||||
|
page = totalPages
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate range for current page
|
||||||
|
start := (page - 1) * pageSize
|
||||||
|
end := start + pageSize
|
||||||
|
if end > filteredTotal {
|
||||||
|
end = filteredTotal
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the page data
|
||||||
|
var pageKeyStatusList []KeyStatus
|
||||||
|
if start < filteredTotal {
|
||||||
|
pageKeyStatusList = filteredKeyStatusList[start:end]
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"success": true,
|
"success": true,
|
||||||
"message": "",
|
"message": "",
|
||||||
"data": MultiKeyStatusResponse{
|
"data": MultiKeyStatusResponse{
|
||||||
Keys: keyStatusList,
|
Keys: pageKeyStatusList,
|
||||||
Total: total,
|
Total: filteredTotal, // Total of filtered results
|
||||||
Page: page,
|
Page: page,
|
||||||
PageSize: pageSize,
|
PageSize: pageSize,
|
||||||
TotalPages: totalPages,
|
TotalPages: totalPages,
|
||||||
EnabledCount: enabledCount,
|
EnabledCount: enabledCount, // Overall statistics
|
||||||
ManualDisabledCount: manualDisabledCount,
|
ManualDisabledCount: manualDisabledCount, // Overall statistics
|
||||||
AutoDisabledCount: autoDisabledCount,
|
AutoDisabledCount: autoDisabledCount, // Overall statistics
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
@@ -1231,8 +1251,6 @@ func ManageMultiKeys(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
channel.ChannelInfo.MultiKeyStatusList[keyIndex] = 2 // disabled
|
channel.ChannelInfo.MultiKeyStatusList[keyIndex] = 2 // disabled
|
||||||
channel.ChannelInfo.MultiKeyDisabledTime[keyIndex] = common.GetTimestamp()
|
|
||||||
channel.ChannelInfo.MultiKeyDisabledReason[keyIndex] = "手动禁用"
|
|
||||||
|
|
||||||
err = channel.Update()
|
err = channel.Update()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1289,6 +1307,77 @@ func ManageMultiKeys(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
|
|
||||||
|
case "enable_all_keys":
|
||||||
|
// 清空所有禁用状态,使所有密钥回到默认启用状态
|
||||||
|
var enabledCount int
|
||||||
|
if channel.ChannelInfo.MultiKeyStatusList != nil {
|
||||||
|
enabledCount = len(channel.ChannelInfo.MultiKeyStatusList)
|
||||||
|
}
|
||||||
|
|
||||||
|
channel.ChannelInfo.MultiKeyStatusList = make(map[int]int)
|
||||||
|
channel.ChannelInfo.MultiKeyDisabledTime = make(map[int]int64)
|
||||||
|
channel.ChannelInfo.MultiKeyDisabledReason = make(map[int]string)
|
||||||
|
|
||||||
|
err = channel.Update()
|
||||||
|
if err != nil {
|
||||||
|
common.ApiError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
model.InitChannelCache()
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"success": true,
|
||||||
|
"message": fmt.Sprintf("已启用 %d 个密钥", enabledCount),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
|
||||||
|
case "disable_all_keys":
|
||||||
|
// 禁用所有启用的密钥
|
||||||
|
if channel.ChannelInfo.MultiKeyStatusList == nil {
|
||||||
|
channel.ChannelInfo.MultiKeyStatusList = make(map[int]int)
|
||||||
|
}
|
||||||
|
if channel.ChannelInfo.MultiKeyDisabledTime == nil {
|
||||||
|
channel.ChannelInfo.MultiKeyDisabledTime = make(map[int]int64)
|
||||||
|
}
|
||||||
|
if channel.ChannelInfo.MultiKeyDisabledReason == nil {
|
||||||
|
channel.ChannelInfo.MultiKeyDisabledReason = make(map[int]string)
|
||||||
|
}
|
||||||
|
|
||||||
|
var disabledCount int
|
||||||
|
for i := 0; i < channel.ChannelInfo.MultiKeySize; i++ {
|
||||||
|
status := 1 // default enabled
|
||||||
|
if s, exists := channel.ChannelInfo.MultiKeyStatusList[i]; exists {
|
||||||
|
status = s
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只禁用当前启用的密钥
|
||||||
|
if status == 1 {
|
||||||
|
channel.ChannelInfo.MultiKeyStatusList[i] = 2 // disabled
|
||||||
|
disabledCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if disabledCount == 0 {
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"success": false,
|
||||||
|
"message": "没有可禁用的密钥",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = channel.Update()
|
||||||
|
if err != nil {
|
||||||
|
common.ApiError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
model.InitChannelCache()
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"success": true,
|
||||||
|
"message": fmt.Sprintf("已禁用 %d 个密钥", disabledCount),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
|
||||||
case "delete_disabled_keys":
|
case "delete_disabled_keys":
|
||||||
keys := channel.GetKeys()
|
keys := channel.GetKeys()
|
||||||
var remainingKeys []string
|
var remainingKeys []string
|
||||||
|
|||||||
@@ -67,18 +67,28 @@ const MultiKeyManageModal = ({
|
|||||||
const [manualDisabledCount, setManualDisabledCount] = useState(0);
|
const [manualDisabledCount, setManualDisabledCount] = useState(0);
|
||||||
const [autoDisabledCount, setAutoDisabledCount] = useState(0);
|
const [autoDisabledCount, setAutoDisabledCount] = useState(0);
|
||||||
|
|
||||||
|
// Filter states
|
||||||
|
const [statusFilter, setStatusFilter] = useState(null); // null=all, 1=enabled, 2=manual_disabled, 3=auto_disabled
|
||||||
|
|
||||||
// Load key status data
|
// Load key status data
|
||||||
const loadKeyStatus = async (page = currentPage, size = pageSize) => {
|
const loadKeyStatus = async (page = currentPage, size = pageSize, status = statusFilter) => {
|
||||||
if (!channel?.id) return;
|
if (!channel?.id) return;
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const res = await API.post('/api/channel/multi_key/manage', {
|
const requestData = {
|
||||||
channel_id: channel.id,
|
channel_id: channel.id,
|
||||||
action: 'get_key_status',
|
action: 'get_key_status',
|
||||||
page: page,
|
page: page,
|
||||||
page_size: size
|
page_size: size
|
||||||
});
|
};
|
||||||
|
|
||||||
|
// Add status filter if specified
|
||||||
|
if (status !== null) {
|
||||||
|
requestData.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await API.post('/api/channel/multi_key/manage', requestData);
|
||||||
|
|
||||||
if (res.data.success) {
|
if (res.data.success) {
|
||||||
const data = res.data.data;
|
const data = res.data.data;
|
||||||
@@ -88,7 +98,7 @@ const MultiKeyManageModal = ({
|
|||||||
setPageSize(data.page_size || 50);
|
setPageSize(data.page_size || 50);
|
||||||
setTotalPages(data.total_pages || 0);
|
setTotalPages(data.total_pages || 0);
|
||||||
|
|
||||||
// Update statistics
|
// Update statistics (these are always the overall statistics)
|
||||||
setEnabledCount(data.enabled_count || 0);
|
setEnabledCount(data.enabled_count || 0);
|
||||||
setManualDisabledCount(data.manual_disabled_count || 0);
|
setManualDisabledCount(data.manual_disabled_count || 0);
|
||||||
setAutoDisabledCount(data.auto_disabled_count || 0);
|
setAutoDisabledCount(data.auto_disabled_count || 0);
|
||||||
@@ -155,6 +165,58 @@ const MultiKeyManageModal = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Enable all disabled keys
|
||||||
|
const handleEnableAll = async () => {
|
||||||
|
setOperationLoading(prev => ({ ...prev, enable_all: true }));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await API.post('/api/channel/multi_key/manage', {
|
||||||
|
channel_id: channel.id,
|
||||||
|
action: 'enable_all_keys'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.data.success) {
|
||||||
|
showSuccess(res.data.message || t('已启用所有密钥'));
|
||||||
|
// Reset to first page after bulk operation
|
||||||
|
setCurrentPage(1);
|
||||||
|
await loadKeyStatus(1, pageSize);
|
||||||
|
onRefresh && onRefresh(); // Refresh parent component
|
||||||
|
} else {
|
||||||
|
showError(res.data.message);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
showError(t('启用所有密钥失败'));
|
||||||
|
} finally {
|
||||||
|
setOperationLoading(prev => ({ ...prev, enable_all: false }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Disable all enabled keys
|
||||||
|
const handleDisableAll = async () => {
|
||||||
|
setOperationLoading(prev => ({ ...prev, disable_all: true }));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await API.post('/api/channel/multi_key/manage', {
|
||||||
|
channel_id: channel.id,
|
||||||
|
action: 'disable_all_keys'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.data.success) {
|
||||||
|
showSuccess(res.data.message || t('已禁用所有密钥'));
|
||||||
|
// Reset to first page after bulk operation
|
||||||
|
setCurrentPage(1);
|
||||||
|
await loadKeyStatus(1, pageSize);
|
||||||
|
onRefresh && onRefresh(); // Refresh parent component
|
||||||
|
} else {
|
||||||
|
showError(res.data.message);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
showError(t('禁用所有密钥失败'));
|
||||||
|
} finally {
|
||||||
|
setOperationLoading(prev => ({ ...prev, disable_all: false }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Delete all disabled keys
|
// Delete all disabled keys
|
||||||
const handleDeleteDisabledKeys = async () => {
|
const handleDeleteDisabledKeys = async () => {
|
||||||
setOperationLoading(prev => ({ ...prev, delete_disabled: true }));
|
setOperationLoading(prev => ({ ...prev, delete_disabled: true }));
|
||||||
@@ -194,6 +256,13 @@ const MultiKeyManageModal = ({
|
|||||||
loadKeyStatus(1, size);
|
loadKeyStatus(1, size);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handle status filter change
|
||||||
|
const handleStatusFilterChange = (status) => {
|
||||||
|
setStatusFilter(status);
|
||||||
|
setCurrentPage(1); // Reset to first page when filter changes
|
||||||
|
loadKeyStatus(1, pageSize, status);
|
||||||
|
};
|
||||||
|
|
||||||
// Effect to load data when modal opens
|
// Effect to load data when modal opens
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (visible && channel?.id) {
|
if (visible && channel?.id) {
|
||||||
@@ -212,6 +281,7 @@ const MultiKeyManageModal = ({
|
|||||||
setEnabledCount(0);
|
setEnabledCount(0);
|
||||||
setManualDisabledCount(0);
|
setManualDisabledCount(0);
|
||||||
setAutoDisabledCount(0);
|
setAutoDisabledCount(0);
|
||||||
|
setStatusFilter(null); // Reset filter
|
||||||
}
|
}
|
||||||
}, [visible]);
|
}, [visible]);
|
||||||
|
|
||||||
@@ -236,15 +306,15 @@ const MultiKeyManageModal = ({
|
|||||||
dataIndex: 'index',
|
dataIndex: 'index',
|
||||||
render: (text) => `#${text}`,
|
render: (text) => `#${text}`,
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
title: t('密钥预览'),
|
// title: t('密钥预览'),
|
||||||
dataIndex: 'key_preview',
|
// dataIndex: 'key_preview',
|
||||||
render: (text) => (
|
// render: (text) => (
|
||||||
<Text code style={{ fontSize: '12px' }}>
|
// <Text code style={{ fontSize: '12px' }}>
|
||||||
{text}
|
// {text}
|
||||||
</Text>
|
// </Text>
|
||||||
),
|
// ),
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
title: t('状态'),
|
title: t('状态'),
|
||||||
dataIndex: 'status',
|
dataIndex: 'status',
|
||||||
@@ -292,33 +362,23 @@ const MultiKeyManageModal = ({
|
|||||||
render: (_, record) => (
|
render: (_, record) => (
|
||||||
<Space>
|
<Space>
|
||||||
{record.status === 1 ? (
|
{record.status === 1 ? (
|
||||||
<Popconfirm
|
<Button
|
||||||
title={t('确定要禁用此密钥吗?')}
|
type='danger'
|
||||||
content={t('禁用后该密钥将不再被使用')}
|
size='small'
|
||||||
onConfirm={() => handleDisableKey(record.index)}
|
loading={operationLoading[`disable_${record.index}`]}
|
||||||
|
onClick={() => handleDisableKey(record.index)}
|
||||||
>
|
>
|
||||||
<Button
|
{t('禁用')}
|
||||||
type='danger'
|
</Button>
|
||||||
size='small'
|
|
||||||
loading={operationLoading[`disable_${record.index}`]}
|
|
||||||
>
|
|
||||||
{t('禁用')}
|
|
||||||
</Button>
|
|
||||||
</Popconfirm>
|
|
||||||
) : (
|
) : (
|
||||||
<Popconfirm
|
<Button
|
||||||
title={t('确定要启用此密钥吗?')}
|
type='primary'
|
||||||
content={t('启用后该密钥将重新被使用')}
|
size='small'
|
||||||
onConfirm={() => handleEnableKey(record.index)}
|
loading={operationLoading[`enable_${record.index}`]}
|
||||||
|
onClick={() => handleEnableKey(record.index)}
|
||||||
>
|
>
|
||||||
<Button
|
{t('启用')}
|
||||||
type='primary'
|
</Button>
|
||||||
size='small'
|
|
||||||
loading={operationLoading[`enable_${record.index}`]}
|
|
||||||
>
|
|
||||||
{t('启用')}
|
|
||||||
</Button>
|
|
||||||
</Popconfirm>
|
|
||||||
)}
|
)}
|
||||||
</Space>
|
</Space>
|
||||||
),
|
),
|
||||||
@@ -347,21 +407,48 @@ const MultiKeyManageModal = ({
|
|||||||
>
|
>
|
||||||
{t('刷新')}
|
{t('刷新')}
|
||||||
</Button>
|
</Button>
|
||||||
{autoDisabledCount > 0 && (
|
<Popconfirm
|
||||||
|
title={t('确定要启用所有密钥吗?')}
|
||||||
|
onConfirm={handleEnableAll}
|
||||||
|
position={'topRight'}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
type='primary'
|
||||||
|
loading={operationLoading.enable_all}
|
||||||
|
>
|
||||||
|
{t('启用全部')}
|
||||||
|
</Button>
|
||||||
|
</Popconfirm>
|
||||||
|
{enabledCount > 0 && (
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title={t('确定要删除所有已自动禁用的密钥吗?')}
|
title={t('确定要禁用所有的密钥吗?')}
|
||||||
content={t('此操作不可撤销,将永久删除已自动禁用的密钥')}
|
onConfirm={handleDisableAll}
|
||||||
onConfirm={handleDeleteDisabledKeys}
|
okType={'danger'}
|
||||||
|
position={'topRight'}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
type='danger'
|
type='danger'
|
||||||
icon={<IconDelete />}
|
loading={operationLoading.disable_all}
|
||||||
loading={operationLoading.delete_disabled}
|
|
||||||
>
|
>
|
||||||
{t('删除自动禁用密钥')}
|
{t('禁用全部')}
|
||||||
</Button>
|
</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
)}
|
)}
|
||||||
|
<Popconfirm
|
||||||
|
title={t('确定要删除所有已自动禁用的密钥吗?')}
|
||||||
|
content={t('此操作不可撤销,将永久删除已自动禁用的密钥')}
|
||||||
|
onConfirm={handleDeleteDisabledKeys}
|
||||||
|
okType={'danger'}
|
||||||
|
position={'topRight'}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
type='danger'
|
||||||
|
icon={<IconDelete />}
|
||||||
|
loading={operationLoading.delete_disabled}
|
||||||
|
>
|
||||||
|
{t('删除自动禁用密钥')}
|
||||||
|
</Button>
|
||||||
|
</Popconfirm>
|
||||||
</Space>
|
</Space>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@@ -391,6 +478,28 @@ const MultiKeyManageModal = ({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Filter Controls */}
|
||||||
|
<div style={{ marginBottom: '16px', display: 'flex', alignItems: 'center', gap: '12px' }}>
|
||||||
|
<Text style={{ fontSize: '14px', fontWeight: '500' }}>{t('状态筛选')}:</Text>
|
||||||
|
<Select
|
||||||
|
value={statusFilter}
|
||||||
|
onChange={handleStatusFilterChange}
|
||||||
|
style={{ width: '120px' }}
|
||||||
|
size='small'
|
||||||
|
placeholder={t('全部状态')}
|
||||||
|
>
|
||||||
|
<Select.Option value={null}>{t('全部状态')}</Select.Option>
|
||||||
|
<Select.Option value={1}>{t('已启用')}</Select.Option>
|
||||||
|
<Select.Option value={2}>{t('手动禁用')}</Select.Option>
|
||||||
|
<Select.Option value={3}>{t('自动禁用')}</Select.Option>
|
||||||
|
</Select>
|
||||||
|
{statusFilter !== null && (
|
||||||
|
<Text type='quaternary' style={{ fontSize: '12px' }}>
|
||||||
|
{t('当前显示 {{count}} 条筛选结果', { count: total })}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Key Status Table */}
|
{/* Key Status Table */}
|
||||||
<Spin spinning={loading}>
|
<Spin spinning={loading}>
|
||||||
{keyStatusList.length > 0 ? (
|
{keyStatusList.length > 0 ? (
|
||||||
|
|||||||
Reference in New Issue
Block a user