-
核心特性
-
-
-
-
-
-
-
多提供商支持
-
- 支持 OpenAI、Azure OpenAI 和 Claude API,轻松切换不同的 AI 服务。
-
-
-
-
-
-
-
-
-
-
-
-
负载均衡
-
- 自动在多个频道间分配请求,确保服务稳定性和高可用性。
-
-
-
-
-
-
-
-
-
-
-
-
用量追踪
-
- 实时监控 token 使用情况,精确计算成本,防止超出配额。
-
-
-
-
-
-
-
-
-
-
-
-
安全管理
-
- 基于令牌的访问控制,为不同用户分配独立的 API 密钥。
-
-
-
-
-
+
+
+ {/* Hero Section */}
+
+
+
+
+
+ 统一 AI 接口网关
+
+
+ 欢迎使用 Awsl One API
+
+
+ 一个强大的 API 代理和管理平台,为您提供统一的 AI 模型接口管理服务。
+ 支持多种 AI 服务提供商,实现智能负载均衡和精确用量追踪。
+
+
+ {isAuthenticated ? (
+ <>
+
+
+ >
+ ) : (
+ <>
+
+
+ >
+ )}
+
-
-
-
+
+
-
-
- 快速开始
- 按照以下步骤开始使用
-
-
-
- -
- 管理员登录:使用管理员令牌登录系统
-
- -
- 配置频道:添加 OpenAI、Azure 或 Claude API 频道
-
- -
- 创建令牌:为您的应用创建 API 令牌
-
- -
- 开始使用:使用生成的令牌调用 API
-
-
-
-
+ {/* Features Grid */}
+
+
核心特性
+
+ {features.map((feature) => (
+
+
+
+
+
+ {feature.title}
+
+ {feature.description}
+
+
+
+ ))}
+
+
+
+ {/* Quick Start */}
+
+
+ 快速开始
+ 按照以下步骤开始使用
+
+
+
+ {steps.map((step, index) => (
+
+ {index < steps.length - 1 && (
+
+ )}
+
+
+ {step.num}
+
+
+
{step.title}
+
{step.desc}
+
+
+
+ ))}
+
+
+
+
)
}
diff --git a/frontend/src/pages/Pricing.tsx b/frontend/src/pages/Pricing.tsx
index b22051d..f9819bc 100644
--- a/frontend/src/pages/Pricing.tsx
+++ b/frontend/src/pages/Pricing.tsx
@@ -2,22 +2,24 @@ import { useState, useEffect } from 'react'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { apiClient } from '@/api/client'
import { PricingConfig } from '@/types'
-import { Card, CardContent, CardHeader } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Textarea } from '@/components/ui/textarea'
import { useToast } from '@/components/ui/use-toast'
-import { Alert, AlertDescription } from '@/components/ui/alert'
+import { cn } from '@/lib/utils'
import {
- Table,
- TableBody,
- TableCell,
- TableHead,
- TableHeader,
- TableRow,
-} from '@/components/ui/table'
-import { Plus, RefreshCw, Trash2, FileJson, FileText, Info } from 'lucide-react'
+ Plus,
+ RefreshCw,
+ Trash2,
+ FileJson,
+ FileText,
+ DollarSign,
+ Check,
+ Info,
+ Search,
+} from 'lucide-react'
+import { PageContainer } from '@/components/ui/page-container'
type EditMode = 'table' | 'json'
@@ -27,12 +29,12 @@ export function Pricing() {
const [pricingRows, setPricingRows] = useState<
Array<{ model: string; input: number; output: number }>
>([])
- const [isInitialized, setIsInitialized] = useState(false)
+ const [searchQuery, setSearchQuery] = useState('')
const { addToast } = useToast()
const queryClient = useQueryClient()
- const { data, isLoading, refetch } = useQuery({
+ const { data, isLoading, isFetching, refetch } = useQuery({
queryKey: ['pricing'],
queryFn: async () => {
const response = await apiClient.getPricing()
@@ -40,9 +42,8 @@ export function Pricing() {
},
})
- // Initialize rows when data first loads - sync server state to local editable state
useEffect(() => {
- if (data && !isInitialized) {
+ if (data) {
const rows = Object.entries(data).map(([model, pricing]) => ({
model,
input: pricing.input,
@@ -50,9 +51,8 @@ export function Pricing() {
}))
setPricingRows(rows)
setJsonValue(JSON.stringify(data, null, 2))
- setIsInitialized(true)
}
- }, [data, isInitialized])
+ }, [data])
const saveMutation = useMutation({
mutationFn: async (config: PricingConfig) => {
@@ -88,7 +88,7 @@ export function Pricing() {
} else {
try {
config = JSON.parse(jsonValue)
- } catch (error) {
+ } catch {
addToast('JSON格式错误', 'error')
return
}
@@ -99,7 +99,6 @@ export function Pricing() {
const toggleEditMode = () => {
if (editMode === 'table') {
- // Switch to JSON
const config: PricingConfig = {}
pricingRows.forEach((row) => {
if (row.model) {
@@ -112,7 +111,6 @@ export function Pricing() {
setJsonValue(JSON.stringify(config, null, 2))
setEditMode('json')
} else {
- // Switch to table
try {
const config = JSON.parse(jsonValue)
const rows = Object.entries(config).map(([model, pricing]: [string, unknown]) => ({
@@ -122,7 +120,7 @@ export function Pricing() {
}))
setPricingRows(rows)
setEditMode('table')
- } catch (error) {
+ } catch {
addToast('JSON格式错误', 'error')
}
}
@@ -136,134 +134,164 @@ export function Pricing() {
setPricingRows(pricingRows.filter((_, i) => i !== index))
}
- const updateRow = (index: number, field: 'model' | 'input' | 'output', value: any) => {
+ const updateRow = (index: number, field: 'model' | 'input' | 'output', value: string | number) => {
const newRows = [...pricingRows]
- newRows[index][field] = value
+ ;(newRows[index] as any)[field] = value
setPricingRows(newRows)
}
+ const filteredRows = pricingRows
+ .map((row, index) => ({ ...row, _i: index }))
+ .filter((row) => row.model.toLowerCase().includes(searchQuery.toLowerCase()))
+
return (
-
-
-
定价管理
+
+
+
+
+
+ }
+ >
+ {/* Info */}
+
+
+
+ 基础配额单位:1M tokens = $1.00。
+ 倍率表示相对成本,如输入倍率 30 = 每百万 token 消耗 $30 配额。
+
-
-
-
-
- {editMode === 'table' && (
-
- )}
-
-