mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-06-16 21:03:58 +08:00
feat(auth-files): merge auth file details into editor modal
This commit is contained in:
@@ -3,13 +3,7 @@ import { Button } from '@/components/ui/Button';
|
||||
import { LoadingSpinner } from '@/components/ui/LoadingSpinner';
|
||||
import { SelectionCheckbox } from '@/components/ui/SelectionCheckbox';
|
||||
import { ToggleSwitch } from '@/components/ui/ToggleSwitch';
|
||||
import {
|
||||
IconBot,
|
||||
IconCode,
|
||||
IconDownload,
|
||||
IconInfo,
|
||||
IconTrash2,
|
||||
} from '@/components/ui/icons';
|
||||
import { IconBot, IconDownload, IconSettings, IconTrash2 } from '@/components/ui/icons';
|
||||
import { ProviderStatusBar } from '@/components/providers/ProviderStatusBar';
|
||||
import type { AuthFileItem } from '@/types';
|
||||
import { resolveAuthProvider } from '@/utils/quota';
|
||||
@@ -44,7 +38,6 @@ export type AuthFileCardProps = {
|
||||
keyStats: KeyStats;
|
||||
statusBarCache: Map<string, AuthFileStatusBarData>;
|
||||
onShowModels: (file: AuthFileItem) => void;
|
||||
onShowDetails: (file: AuthFileItem) => void;
|
||||
onDownload: (name: string) => void;
|
||||
onOpenPrefixProxyEditor: (file: AuthFileItem) => void;
|
||||
onDelete: (name: string) => void;
|
||||
@@ -71,7 +64,6 @@ export function AuthFileCard(props: AuthFileCardProps) {
|
||||
keyStats,
|
||||
statusBarCache,
|
||||
onShowModels,
|
||||
onShowDetails,
|
||||
onDownload,
|
||||
onOpenPrefixProxyEditor,
|
||||
onDelete,
|
||||
@@ -95,13 +87,13 @@ export function AuthFileCard(props: AuthFileCardProps) {
|
||||
? styles.antigravityCard
|
||||
: quotaType === 'claude'
|
||||
? styles.claudeCard
|
||||
: quotaType === 'codex'
|
||||
? styles.codexCard
|
||||
: quotaType === 'gemini-cli'
|
||||
? styles.geminiCliCard
|
||||
: quotaType === 'kimi'
|
||||
? styles.kimiCard
|
||||
: '';
|
||||
: quotaType === 'codex'
|
||||
? styles.codexCard
|
||||
: quotaType === 'gemini-cli'
|
||||
? styles.geminiCliCard
|
||||
: quotaType === 'kimi'
|
||||
? styles.kimiCard
|
||||
: '';
|
||||
|
||||
const rawAuthIndex = file['auth_index'] ?? file.authIndex;
|
||||
const authIndexKey = normalizeAuthIndex(rawAuthIndex);
|
||||
@@ -153,7 +145,8 @@ export function AuthFileCard(props: AuthFileCardProps) {
|
||||
</span>
|
||||
{priorityValue !== undefined && (
|
||||
<span className={styles.priorityBadge}>
|
||||
{t('auth_files.priority_display')}: <span className={styles.priorityValue}>{priorityValue}</span>
|
||||
{t('auth_files.priority_display')}:{' '}
|
||||
<span className={styles.priorityValue}>{priorityValue}</span>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
@@ -205,16 +198,6 @@ export function AuthFileCard(props: AuthFileCardProps) {
|
||||
)}
|
||||
{!isRuntimeOnly && (
|
||||
<>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={() => onShowDetails(file)}
|
||||
className={styles.iconButton}
|
||||
title={t('common.info', { defaultValue: '关于' })}
|
||||
disabled={disableControls}
|
||||
>
|
||||
<IconInfo className={styles.actionIcon} size={16} />
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
@@ -233,7 +216,7 @@ export function AuthFileCard(props: AuthFileCardProps) {
|
||||
title={t('auth_files.prefix_proxy_button')}
|
||||
disabled={disableControls}
|
||||
>
|
||||
<IconCode className={styles.actionIcon} size={16} />
|
||||
<IconSettings className={styles.actionIcon} size={16} />
|
||||
</Button>
|
||||
<Button
|
||||
variant="danger"
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Modal } from '@/components/ui/Modal';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import type { AuthFileItem } from '@/types';
|
||||
import styles from '@/pages/AuthFilesPage.module.scss';
|
||||
|
||||
export type AuthFileDetailModalProps = {
|
||||
open: boolean;
|
||||
file: AuthFileItem | null;
|
||||
onClose: () => void;
|
||||
onCopyText: (text: string) => void;
|
||||
};
|
||||
|
||||
export function AuthFileDetailModal({ open, file, onClose, onCopyText }: AuthFileDetailModalProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
title={file?.name || t('auth_files.title_section')}
|
||||
footer={
|
||||
<>
|
||||
<Button variant="secondary" onClick={onClose}>
|
||||
{t('common.close')}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (!file) return;
|
||||
const text = JSON.stringify(file, null, 2);
|
||||
onCopyText(text);
|
||||
}}
|
||||
>
|
||||
{t('common.copy')}
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
>
|
||||
{file && (
|
||||
<div className={styles.detailContent}>
|
||||
<pre className={styles.jsonContent}>{JSON.stringify(file, null, 2)}</pre>
|
||||
</div>
|
||||
)}
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,13 +17,24 @@ export type AuthFilesPrefixProxyEditorModalProps = {
|
||||
updatedText: string;
|
||||
dirty: boolean;
|
||||
onClose: () => void;
|
||||
onCopyText: (text: string) => void | Promise<void>;
|
||||
onSave: () => void;
|
||||
onChange: (field: PrefixProxyEditorField, value: PrefixProxyEditorFieldValue) => void;
|
||||
};
|
||||
|
||||
export function AuthFilesPrefixProxyEditorModal(props: AuthFilesPrefixProxyEditorModalProps) {
|
||||
const { t } = useTranslation();
|
||||
const { disableControls, editor, updatedText, dirty, onClose, onSave, onChange } = props;
|
||||
const { disableControls, editor, updatedText, dirty, onClose, onCopyText, onSave, onChange } =
|
||||
props;
|
||||
const formatJsonText = (text: string) => {
|
||||
if (!text) return '';
|
||||
try {
|
||||
return JSON.stringify(JSON.parse(text), null, 2);
|
||||
} catch {
|
||||
return text;
|
||||
}
|
||||
};
|
||||
const previewText = formatJsonText(updatedText);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
@@ -39,7 +50,17 @@ export function AuthFilesPrefixProxyEditorModal(props: AuthFilesPrefixProxyEdito
|
||||
footer={
|
||||
<>
|
||||
<Button variant="secondary" onClick={onClose} disabled={editor?.saving === true}>
|
||||
{t('common.cancel')}
|
||||
{dirty ? t('common.cancel') : t('common.close')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
if (!updatedText) return;
|
||||
void onCopyText(updatedText);
|
||||
}}
|
||||
disabled={editor?.saving === true || !updatedText}
|
||||
>
|
||||
{t('common.copy')}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={onSave}
|
||||
@@ -61,6 +82,17 @@ export function AuthFilesPrefixProxyEditorModal(props: AuthFilesPrefixProxyEdito
|
||||
) : (
|
||||
<>
|
||||
{editor.error && <div className={styles.prefixProxyError}>{editor.error}</div>}
|
||||
<div className={styles.prefixProxyJsonWrapper}>
|
||||
<label className={styles.prefixProxyLabel}>
|
||||
{t('auth_files.prefix_proxy_info_label')}
|
||||
</label>
|
||||
<textarea
|
||||
className={styles.prefixProxyTextarea}
|
||||
rows={8}
|
||||
readOnly
|
||||
value={editor.fileInfoText}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.prefixProxyJsonWrapper}>
|
||||
<label className={styles.prefixProxyLabel}>
|
||||
{t('auth_files.prefix_proxy_source_label')}
|
||||
@@ -69,7 +101,7 @@ export function AuthFilesPrefixProxyEditorModal(props: AuthFilesPrefixProxyEdito
|
||||
className={styles.prefixProxyTextarea}
|
||||
rows={10}
|
||||
readOnly
|
||||
value={updatedText}
|
||||
value={previewText}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.prefixProxyFields}>
|
||||
|
||||
@@ -27,6 +27,7 @@ export type PrefixProxyEditorFieldValue = string | boolean;
|
||||
|
||||
export type PrefixProxyEditorState = {
|
||||
fileName: string;
|
||||
fileInfoText: string;
|
||||
isCodexFile: boolean;
|
||||
loading: boolean;
|
||||
saving: boolean;
|
||||
@@ -54,7 +55,7 @@ export type UseAuthFilesPrefixProxyEditorResult = {
|
||||
prefixProxyEditor: PrefixProxyEditorState | null;
|
||||
prefixProxyUpdatedText: string;
|
||||
prefixProxyDirty: boolean;
|
||||
openPrefixProxyEditor: (file: Pick<AuthFileItem, 'name' | 'type' | 'provider'>) => Promise<void>;
|
||||
openPrefixProxyEditor: (file: AuthFileItem) => Promise<void>;
|
||||
closePrefixProxyEditor: () => void;
|
||||
handlePrefixProxyChange: (
|
||||
field: PrefixProxyEditorField,
|
||||
@@ -127,7 +128,7 @@ export function useAuthFilesPrefixProxyEditor(
|
||||
setPrefixProxyEditor(null);
|
||||
};
|
||||
|
||||
const openPrefixProxyEditor = async (file: Pick<AuthFileItem, 'name' | 'type' | 'provider'>) => {
|
||||
const openPrefixProxyEditor = async (file: AuthFileItem) => {
|
||||
const name = file.name;
|
||||
const normalizedType = String(file.type ?? '')
|
||||
.trim()
|
||||
@@ -145,6 +146,7 @@ export function useAuthFilesPrefixProxyEditor(
|
||||
|
||||
setPrefixProxyEditor({
|
||||
fileName: name,
|
||||
fileInfoText: JSON.stringify(file, null, 2),
|
||||
isCodexFile,
|
||||
loading: true,
|
||||
saving: false,
|
||||
|
||||
@@ -552,9 +552,10 @@
|
||||
"batch_deselect": "Deselect",
|
||||
"batch_enable": "Enable",
|
||||
"batch_disable": "Disable",
|
||||
"prefix_proxy_button": "Edit Auth Fields",
|
||||
"auth_field_editor_title": "Edit Auth Fields - {{name}}",
|
||||
"prefix_proxy_button": "Auth File Details / Edit",
|
||||
"auth_field_editor_title": "Auth File Details / Edit - {{name}}",
|
||||
"prefix_proxy_loading": "Loading auth file...",
|
||||
"prefix_proxy_info_label": "Auth file info (info)",
|
||||
"prefix_proxy_source_label": "Auth file JSON (preview)",
|
||||
"prefix_label": "Prefix (prefix)",
|
||||
"proxy_url_label": "Proxy URL (proxy_url)",
|
||||
|
||||
@@ -552,9 +552,10 @@
|
||||
"batch_deselect": "Отменить",
|
||||
"batch_enable": "Включить",
|
||||
"batch_disable": "Отключить",
|
||||
"prefix_proxy_button": "Редактировать поля файла авторизации",
|
||||
"auth_field_editor_title": "Редактировать поля файла авторизации - {{name}}",
|
||||
"prefix_proxy_button": "Просмотр / редактирование файла авторизации",
|
||||
"auth_field_editor_title": "Просмотр / редактирование файла авторизации - {{name}}",
|
||||
"prefix_proxy_loading": "Загрузка файла авторизации...",
|
||||
"prefix_proxy_info_label": "Информация о файле авторизации (info)",
|
||||
"prefix_proxy_source_label": "JSON файла авторизации (предпросмотр)",
|
||||
"prefix_label": "Префикс (prefix)",
|
||||
"proxy_url_label": "URL прокси (proxy_url)",
|
||||
|
||||
@@ -552,9 +552,10 @@
|
||||
"batch_deselect": "取消选择",
|
||||
"batch_enable": "启用",
|
||||
"batch_disable": "禁用",
|
||||
"prefix_proxy_button": "编辑认证文件字段",
|
||||
"auth_field_editor_title": "编辑认证文件字段 - {{name}}",
|
||||
"prefix_proxy_button": "认证文件详情 / 编辑",
|
||||
"auth_field_editor_title": "认证文件详情 / 编辑 - {{name}}",
|
||||
"prefix_proxy_loading": "正在加载认证文件...",
|
||||
"prefix_proxy_info_label": "认证文件信息(info)",
|
||||
"prefix_proxy_source_label": "认证文件 JSON(预览)",
|
||||
"prefix_label": "前缀(prefix)",
|
||||
"proxy_url_label": "代理 URL(proxy_url)",
|
||||
|
||||
@@ -37,7 +37,6 @@ import {
|
||||
type ResolvedTheme,
|
||||
} from '@/features/authFiles/constants';
|
||||
import { AuthFileCard } from '@/features/authFiles/components/AuthFileCard';
|
||||
import { AuthFileDetailModal } from '@/features/authFiles/components/AuthFileDetailModal';
|
||||
import { AuthFileModelsModal } from '@/features/authFiles/components/AuthFileModelsModal';
|
||||
import { AuthFilesPrefixProxyEditorModal } from '@/features/authFiles/components/AuthFilesPrefixProxyEditorModal';
|
||||
import { OAuthExcludedCard } from '@/features/authFiles/components/OAuthExcludedCard';
|
||||
@@ -64,7 +63,6 @@ import {
|
||||
type AuthFilesSortMode,
|
||||
} from '@/features/authFiles/uiState';
|
||||
import { useAuthStore, useNotificationStore, useThemeStore } from '@/stores';
|
||||
import type { AuthFileItem } from '@/types';
|
||||
import styles from './AuthFilesPage.module.scss';
|
||||
|
||||
const easePower3Out = (progress: number) => 1 - (1 - progress) ** 4;
|
||||
@@ -109,8 +107,6 @@ export function AuthFilesPage() {
|
||||
const [page, setPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(9);
|
||||
const [pageSizeInput, setPageSizeInput] = useState('9');
|
||||
const [detailModalOpen, setDetailModalOpen] = useState(false);
|
||||
const [selectedFile, setSelectedFile] = useState<AuthFileItem | null>(null);
|
||||
const [viewMode, setViewMode] = useState<'diagram' | 'list'>('list');
|
||||
const [sortMode, setSortMode] = useState<AuthFilesSortMode>('default');
|
||||
const [batchActionBarVisible, setBatchActionBarVisible] = useState(false);
|
||||
@@ -374,11 +370,6 @@ export function AuthFilesPage() {
|
||||
);
|
||||
const selectedNames = useMemo(() => Array.from(selectedFiles), [selectedFiles]);
|
||||
|
||||
const showDetails = (file: AuthFileItem) => {
|
||||
setSelectedFile(file);
|
||||
setDetailModalOpen(true);
|
||||
};
|
||||
|
||||
const copyTextWithNotification = useCallback(
|
||||
async (text: string) => {
|
||||
const copied = await copyToClipboard(text);
|
||||
@@ -702,7 +693,6 @@ export function AuthFilesPage() {
|
||||
keyStats={keyStats}
|
||||
statusBarCache={statusBarCache}
|
||||
onShowModels={showModels}
|
||||
onShowDetails={showDetails}
|
||||
onDownload={handleDownload}
|
||||
onOpenPrefixProxyEditor={openPrefixProxyEditor}
|
||||
onDelete={handleDelete}
|
||||
@@ -768,13 +758,6 @@ export function AuthFilesPage() {
|
||||
onDeleteAlias={handleDeleteAlias}
|
||||
/>
|
||||
|
||||
<AuthFileDetailModal
|
||||
open={detailModalOpen}
|
||||
file={selectedFile}
|
||||
onClose={() => setDetailModalOpen(false)}
|
||||
onCopyText={copyTextWithNotification}
|
||||
/>
|
||||
|
||||
<AuthFileModelsModal
|
||||
open={modelsModalOpen}
|
||||
fileName={modelsFileName}
|
||||
@@ -793,6 +776,7 @@ export function AuthFilesPage() {
|
||||
updatedText={prefixProxyUpdatedText}
|
||||
dirty={prefixProxyDirty}
|
||||
onClose={closePrefixProxyEditor}
|
||||
onCopyText={copyTextWithNotification}
|
||||
onSave={handlePrefixProxySave}
|
||||
onChange={handlePrefixProxyChange}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user