From ada434fb20459f4f18c0643a097892c87109675a Mon Sep 17 00:00:00 2001 From: t0ng7u Date: Sun, 10 Aug 2025 00:55:18 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20refactor:=20MultiKeyManageModal:?= =?UTF-8?q?=20cleaner=20stats=20UI,=20remove=20chart,=20integrate=20toolba?= =?UTF-8?q?r/pagination,=20and=20improve=20UX?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace custom dots with Semi Badge types (success/danger/warning); add compact Progress bars - Remove pie chart and related deps/config; move total key count and mode tags into the modal title - Rework header using Row/Col; three equal stat cards (enabled/manual-disabled/auto-disabled) - Integrate toolbar into Table title; wrap content with Card; use Table’s native empty state - Make “Enable All” conditional (hidden when all keys are enabled), mirroring “Disable All” - Unify numeric typography (current/total same size) for better readability - Default page size set to 10; fallback to 10 when backend page_size is absent; page-size options: 10/20/50/100 - Cleanup imports and dead code (remove VChart and pie-spec logic) - Minor spacing polish (extra bottom margin before table), no footer buttons --- .../table/channels/ChannelsColumnDefs.js | 2 +- .../channels/modals/MultiKeyManageModal.jsx | 432 +++++++++--------- web/src/i18n/locales/en.json | 19 +- 3 files changed, 235 insertions(+), 218 deletions(-) diff --git a/web/src/components/table/channels/ChannelsColumnDefs.js b/web/src/components/table/channels/ChannelsColumnDefs.js index 18cb5700..06a23024 100644 --- a/web/src/components/table/channels/ChannelsColumnDefs.js +++ b/web/src/components/table/channels/ChannelsColumnDefs.js @@ -544,7 +544,7 @@ export const getChannelsColumns = ({ menu={[ { node: 'item', - name: t('多key管理'), + name: t('多密钥管理'), onClick: () => { setCurrentMultiKeyChannel(record); setShowMultiKeyManageModal(true); diff --git a/web/src/components/table/channels/modals/MultiKeyManageModal.jsx b/web/src/components/table/channels/modals/MultiKeyManageModal.jsx index 89ab790f..e185a1b8 100644 --- a/web/src/components/table/channels/modals/MultiKeyManageModal.jsx +++ b/web/src/components/table/channels/modals/MultiKeyManageModal.jsx @@ -30,20 +30,17 @@ import { Popconfirm, Empty, Spin, - Banner, Select, - Pagination + Row, + Col, + Badge, + Progress, + Card } from '@douyinfe/semi-ui'; -import { - IconRefresh, - IconDelete, - IconClose, - IconSave, - IconSetting -} from '@douyinfe/semi-icons'; +import { IllustrationNoResult, IllustrationNoResultDark } from '@douyinfe/semi-illustrations'; import { API, showError, showSuccess, timestamp2string } from '../../../../helpers/index.js'; -const { Text, Title } = Typography; +const { Text } = Typography; const MultiKeyManageModal = ({ visible, @@ -55,13 +52,13 @@ const MultiKeyManageModal = ({ const [loading, setLoading] = useState(false); const [keyStatusList, setKeyStatusList] = useState([]); const [operationLoading, setOperationLoading] = useState({}); - + // Pagination states const [currentPage, setCurrentPage] = useState(1); - const [pageSize, setPageSize] = useState(50); + const [pageSize, setPageSize] = useState(10); const [total, setTotal] = useState(0); const [totalPages, setTotalPages] = useState(0); - + // Statistics states const [enabledCount, setEnabledCount] = useState(0); const [manualDisabledCount, setManualDisabledCount] = useState(0); @@ -73,7 +70,7 @@ const MultiKeyManageModal = ({ // Load key status data const loadKeyStatus = async (page = currentPage, size = pageSize, status = statusFilter) => { if (!channel?.id) return; - + setLoading(true); try { const requestData = { @@ -82,22 +79,22 @@ const MultiKeyManageModal = ({ page: page, 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) { const data = res.data.data; setKeyStatusList(data.keys || []); setTotal(data.total || 0); setCurrentPage(data.page || 1); - setPageSize(data.page_size || 50); + setPageSize(data.page_size || 10); setTotalPages(data.total_pages || 0); - + // Update statistics (these are always the overall statistics) setEnabledCount(data.enabled_count || 0); setManualDisabledCount(data.manual_disabled_count || 0); @@ -117,14 +114,14 @@ const MultiKeyManageModal = ({ const handleDisableKey = async (keyIndex) => { const operationId = `disable_${keyIndex}`; setOperationLoading(prev => ({ ...prev, [operationId]: true })); - + try { const res = await API.post('/api/channel/multi_key/manage', { channel_id: channel.id, action: 'disable_key', key_index: keyIndex }); - + if (res.data.success) { showSuccess(t('密钥已禁用')); await loadKeyStatus(currentPage, pageSize); // Reload current page @@ -143,14 +140,14 @@ const MultiKeyManageModal = ({ const handleEnableKey = async (keyIndex) => { const operationId = `enable_${keyIndex}`; setOperationLoading(prev => ({ ...prev, [operationId]: true })); - + try { const res = await API.post('/api/channel/multi_key/manage', { channel_id: channel.id, action: 'enable_key', key_index: keyIndex }); - + if (res.data.success) { showSuccess(t('密钥已启用')); await loadKeyStatus(currentPage, pageSize); // Reload current page @@ -168,13 +165,13 @@ 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 @@ -194,13 +191,13 @@ const MultiKeyManageModal = ({ // 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 @@ -220,13 +217,13 @@ const MultiKeyManageModal = ({ // Delete all disabled keys const handleDeleteDisabledKeys = async () => { setOperationLoading(prev => ({ ...prev, delete_disabled: true })); - + try { const res = await API.post('/api/channel/multi_key/manage', { channel_id: channel.id, action: 'delete_disabled_keys' }); - + if (res.data.success) { showSuccess(res.data.message); // Reset to first page after deletion as data structure might change @@ -285,17 +282,24 @@ const MultiKeyManageModal = ({ } }, [visible]); + // Percentages for progress display + const enabledPercent = total > 0 ? Math.round((enabledCount / total) * 100) : 0; + const manualDisabledPercent = total > 0 ? Math.round((manualDisabledCount / total) * 100) : 0; + const autoDisabledPercent = total > 0 ? Math.round((autoDisabledCount / total) * 100) : 0; + + // 取消饼图:不再需要图表数据与配置 + // Get status tag component const renderStatusTag = (status) => { switch (status) { case 1: - return {t('已启用')}; + return {t('已启用')}; case 2: - return {t('已禁用')}; + return {t('已禁用')}; case 3: - return {t('自动禁用')}; + return {t('自动禁用')}; default: - return {t('未知状态')}; + return {t('未知状态')}; } }; @@ -318,13 +322,11 @@ const MultiKeyManageModal = ({ { title: t('状态'), dataIndex: 'status', - width: 100, render: (status) => renderStatusTag(status), }, { title: t('禁用原因'), dataIndex: 'reason', - width: 220, render: (reason, record) => { if (record.status === 1 || !reason) { return -; @@ -341,7 +343,6 @@ const MultiKeyManageModal = ({ { title: t('禁用时间'), dataIndex: 'disabled_time', - width: 150, render: (time, record) => { if (record.status === 1 || !time) { return -; @@ -358,7 +359,8 @@ const MultiKeyManageModal = ({ { title: t('操作'), key: 'action', - width: 120, + fixed: 'right', + width: 100, render: (_, record) => ( {record.status === 1 ? ( @@ -389,196 +391,194 @@ const MultiKeyManageModal = ({ - - {t('多密钥管理')} - {channel?.name} + {t('多密钥管理')} + {channel?.name && ( + {channel.name} + )} + + {t('总密钥数')}: {total} + + {channel?.channel_info?.multi_key_mode && ( + + {channel.channel_info.multi_key_mode === 'random' ? t('随机模式') : t('轮询模式')} + + )} } visible={visible} onCancel={onCancel} width={900} - footer={ - - - - - - - {enabledCount > 0 && ( - - - - )} - - - - - } + footer={null} > -
- {/* Statistics Banner */} - - - {t('总共 {{total}} 个密钥,{{enabled}} 个已启用,{{manual}} 个手动禁用,{{auto}} 个自动禁用', { - total: total, - enabled: enabledCount, - manual: manualDisabledCount, - auto: autoDisabledCount - })} - - {channel?.channel_info?.multi_key_mode && ( -
- - {t('多密钥模式')}: {channel.channel_info.multi_key_mode === 'random' ? t('随机') : t('轮询')} - +
+ {/* Stats & Mode */} +
+ + +
+
+ + {t('已启用')}
- )} -
- } - /> - - {/* Filter Controls */} -
- {t('状态筛选')}: - - {statusFilter !== null && ( - - {t('当前显示 {{count}} 条筛选结果', { count: total })} - - )} +
+ {enabledCount} + / {total} +
+ +
+ + +
+
+ + {t('手动禁用')} +
+
+ {manualDisabledCount} + / {total} +
+ +
+ + +
+
+ + {t('自动禁用')} +
+
+ {autoDisabledCount} + / {total} +
+ +
+ +
- {/* Key Status Table */} -
+ {/* Table */} +
- {keyStatusList.length > 0 ? ( -
-
- - - - {/* Pagination */} - {total > 0 && ( -
- - {t('显示第 {{start}}-{{end}} 条,共 {{total}} 条', { - start: (currentPage - 1) * pageSize + 1, - end: Math.min(currentPage * pageSize, total), - total: total - })} - - -
- - {t('每页显示')}: - - - - - t('第 {{current}} / {{total}} 页', { - current: currentPage, - total: totalPages - }) - } - /> -
-
+ +
( + + + + + + + + + + + + {(manualDisabledCount + autoDisabledCount) > 0 && ( + + + + )} + {enabledCount > 0 && ( + + + + )} + + + + + + )} - - ) : ( - !loading && ( - - ) - )} + columns={columns} + dataSource={keyStatusList} + pagination={{ + currentPage: currentPage, + pageSize: pageSize, + total: total, + showSizeChanger: true, + showQuickJumper: true, + pageSizeOptions: ['10', '20', '50', '100'], + onChange: (page, size) => { + setCurrentPage(page); + loadKeyStatus(page, size); + }, + onShowSizeChange: (current, size) => { + setCurrentPage(1); + handlePageSizeChange(size); + } + }} + size='small' + bordered={false} + rowKey='index' + scroll={{ x: 'max-content' }} + empty={ + } + darkModeImage={} + title={t('暂无密钥数据')} + description={t('请检查渠道配置或刷新重试')} + style={{ padding: 30 }} + /> + } + /> + diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index 3be43cca..22a7d6b9 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -1890,5 +1890,22 @@ "未知供应商": "Unknown", "共 {{count}} 个模型": "{{count}} models", "倍率信息": "Ratio information", - "倍率是用于系统计算不同模型的最终价格用的,如果您不理解倍率,请忽略": "The ratio is used to calculate the final price of different models in the system. If you do not understand the ratio, please ignore it." + "倍率是用于系统计算不同模型的最终价格用的,如果您不理解倍率,请忽略": "The ratio is used to calculate the final price of different models in the system. If you do not understand the ratio, please ignore it.", + "多密钥管理": "Multi-key management", + "总密钥数": "Total key count", + "随机模式": "Random mode", + "轮询模式": "Polling mode", + "手动禁用": "Manually disabled", + "自动禁用": "Auto disabled", + "暂无密钥数据": "No key data", + "请检查渠道配置或刷新重试": "Please check the channel configuration or refresh and try again", + "全部状态": "All status", + "索引": "Index", + "禁用原因": "Disable reason", + "禁用时间": "Disable time", + "确定要启用所有密钥吗?": "Are you sure you want to enable all keys?", + "确定要禁用所有的密钥吗?": "Are you sure you want to disable all keys?", + "确定要删除所有已自动禁用的密钥吗?": "Are you sure you want to delete all automatically disabled keys?", + "此操作不可撤销,将永久删除已自动禁用的密钥": "This operation cannot be undone, and all automatically disabled keys will be permanently deleted.", + "删除自动禁用密钥": "Delete auto disabled keys" } \ No newline at end of file