mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-02-19 11:10:49 +08:00
feat(usage): use modal dialog for editing model prices
This commit is contained in:
@@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { Card } from '@/components/ui/Card';
|
import { Card } from '@/components/ui/Card';
|
||||||
import { Button } from '@/components/ui/Button';
|
import { Button } from '@/components/ui/Button';
|
||||||
import { Input } from '@/components/ui/Input';
|
import { Input } from '@/components/ui/Input';
|
||||||
|
import { Modal } from '@/components/ui/Modal';
|
||||||
import { Select } from '@/components/ui/Select';
|
import { Select } from '@/components/ui/Select';
|
||||||
import type { ModelPrice } from '@/utils/usage';
|
import type { ModelPrice } from '@/utils/usage';
|
||||||
import styles from '@/pages/UsagePage.module.scss';
|
import styles from '@/pages/UsagePage.module.scss';
|
||||||
@@ -20,11 +21,18 @@ export function PriceSettingsCard({
|
|||||||
}: PriceSettingsCardProps) {
|
}: PriceSettingsCardProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
// Add form state
|
||||||
const [selectedModel, setSelectedModel] = useState('');
|
const [selectedModel, setSelectedModel] = useState('');
|
||||||
const [promptPrice, setPromptPrice] = useState('');
|
const [promptPrice, setPromptPrice] = useState('');
|
||||||
const [completionPrice, setCompletionPrice] = useState('');
|
const [completionPrice, setCompletionPrice] = useState('');
|
||||||
const [cachePrice, setCachePrice] = useState('');
|
const [cachePrice, setCachePrice] = useState('');
|
||||||
|
|
||||||
|
// Edit modal state
|
||||||
|
const [editModel, setEditModel] = useState<string | null>(null);
|
||||||
|
const [editPrompt, setEditPrompt] = useState('');
|
||||||
|
const [editCompletion, setEditCompletion] = useState('');
|
||||||
|
const [editCache, setEditCache] = useState('');
|
||||||
|
|
||||||
const handleSavePrice = () => {
|
const handleSavePrice = () => {
|
||||||
if (!selectedModel) return;
|
if (!selectedModel) return;
|
||||||
const prompt = parseFloat(promptPrice) || 0;
|
const prompt = parseFloat(promptPrice) || 0;
|
||||||
@@ -44,12 +52,22 @@ export function PriceSettingsCard({
|
|||||||
onPricesChange(newPrices);
|
onPricesChange(newPrices);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEditPrice = (model: string) => {
|
const handleOpenEdit = (model: string) => {
|
||||||
const price = modelPrices[model];
|
const price = modelPrices[model];
|
||||||
setSelectedModel(model);
|
setEditModel(model);
|
||||||
setPromptPrice(price?.prompt?.toString() || '');
|
setEditPrompt(price?.prompt?.toString() || '');
|
||||||
setCompletionPrice(price?.completion?.toString() || '');
|
setEditCompletion(price?.completion?.toString() || '');
|
||||||
setCachePrice(price?.cache?.toString() || '');
|
setEditCache(price?.cache?.toString() || '');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSaveEdit = () => {
|
||||||
|
if (!editModel) return;
|
||||||
|
const prompt = parseFloat(editPrompt) || 0;
|
||||||
|
const completion = parseFloat(editCompletion) || 0;
|
||||||
|
const cache = editCache.trim() === '' ? prompt : parseFloat(editCache) || 0;
|
||||||
|
const newPrices = { ...modelPrices, [editModel]: { prompt, completion, cache } };
|
||||||
|
onPricesChange(newPrices);
|
||||||
|
setEditModel(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleModelSelect = (value: string) => {
|
const handleModelSelect = (value: string) => {
|
||||||
@@ -147,7 +165,7 @@ export function PriceSettingsCard({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.priceActions}>
|
<div className={styles.priceActions}>
|
||||||
<Button variant="secondary" size="sm" onClick={() => handleEditPrice(model)}>
|
<Button variant="secondary" size="sm" onClick={() => handleOpenEdit(model)}>
|
||||||
{t('common.edit')}
|
{t('common.edit')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="danger" size="sm" onClick={() => handleDeletePrice(model)}>
|
<Button variant="danger" size="sm" onClick={() => handleDeletePrice(model)}>
|
||||||
@@ -162,6 +180,57 @@ export function PriceSettingsCard({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Edit Modal */}
|
||||||
|
<Modal
|
||||||
|
open={editModel !== null}
|
||||||
|
title={editModel ?? ''}
|
||||||
|
onClose={() => setEditModel(null)}
|
||||||
|
footer={
|
||||||
|
<div className={styles.priceActions}>
|
||||||
|
<Button variant="secondary" onClick={() => setEditModel(null)}>
|
||||||
|
{t('common.cancel')}
|
||||||
|
</Button>
|
||||||
|
<Button variant="primary" onClick={handleSaveEdit}>
|
||||||
|
{t('common.save')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
width={420}
|
||||||
|
>
|
||||||
|
<div className={styles.editModalBody}>
|
||||||
|
<div className={styles.formField}>
|
||||||
|
<label>{t('usage_stats.model_price_prompt')} ($/1M)</label>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
value={editPrompt}
|
||||||
|
onChange={(e) => setEditPrompt(e.target.value)}
|
||||||
|
placeholder="0.00"
|
||||||
|
step="0.0001"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={styles.formField}>
|
||||||
|
<label>{t('usage_stats.model_price_completion')} ($/1M)</label>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
value={editCompletion}
|
||||||
|
onChange={(e) => setEditCompletion(e.target.value)}
|
||||||
|
placeholder="0.00"
|
||||||
|
step="0.0001"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={styles.formField}>
|
||||||
|
<label>{t('usage_stats.model_price_cache')} ($/1M)</label>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
value={editCache}
|
||||||
|
onChange={(e) => setEditCache(e.target.value)}
|
||||||
|
placeholder="0.00"
|
||||||
|
step="0.0001"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -809,6 +809,12 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.editModalBody {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
// Chart Section (80%比例)
|
// Chart Section (80%比例)
|
||||||
.chartSection {
|
.chartSection {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
Reference in New Issue
Block a user