feat(routes): 添加新频道和令牌的受保护路由
feat(channels): 支持创建模式,优化表单处理逻辑 feat(tokens): 支持创建模式,优化表单处理逻辑 fix(sidebar): 修复导航链接的激活状态判断
This commit is contained in:
1 parent
7ff59e153a
commit
d5f23fe9ed
4 files changed
+77
-21
No files matched your search
@@ -77,6 +77,14 @@ function App() {
|
||||
</ProtectedRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/channels/new"
|
||||
element={
|
||||
<ProtectedRoute>
|
||||
<Channels createMode />
|
||||
</ProtectedRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/tokens"
|
||||
element={
|
||||
@@ -85,6 +93,14 @@ function App() {
|
||||
</ProtectedRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/tokens/new"
|
||||
element={
|
||||
<ProtectedRoute>
|
||||
<Tokens createMode />
|
||||
</ProtectedRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/pricing"
|
||||
element={
|
||||
|
||||
@@ -80,7 +80,7 @@ export function Sidebar({
|
||||
};
|
||||
|
||||
const NavLink = ({ item }: { item: NavItem }) => {
|
||||
const isActive = location.pathname === item.href;
|
||||
const isActive = location.pathname === item.href || location.pathname.startsWith(`${item.href}/`);
|
||||
return (
|
||||
<Link
|
||||
to={item.href}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { apiClient } from '@/api/client'
|
||||
import { Channel, ChannelConfig, ChannelModelMapping } from '@/types'
|
||||
import { Card, CardContent } from '@/components/ui/card'
|
||||
@@ -365,8 +366,9 @@ const getChannelEndpointPreview = (type: string | undefined, endpoint: string):
|
||||
return buildFallbackEndpointPreview(trimmedEndpoint, requestPath)
|
||||
}
|
||||
|
||||
export function Channels() {
|
||||
const [view, setView] = useState<'list' | 'form'>('list')
|
||||
export function Channels({ createMode = false }: { createMode?: boolean }) {
|
||||
const navigate = useNavigate()
|
||||
const [view, setView] = useState<'list' | 'form'>(createMode ? 'form' : 'list')
|
||||
const [editMode, setEditMode] = useState<EditMode>('form')
|
||||
const [modelEditorMode, setModelEditorMode] = useState<ModelEditorMode>('visual')
|
||||
const [editingKey, setEditingKey] = useState<string | null>(null)
|
||||
@@ -426,8 +428,7 @@ export function Channels() {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['channels'] })
|
||||
addToast(editingKey ? '渠道更新成功' : '渠道添加成功', 'success')
|
||||
resetForm()
|
||||
setView('list')
|
||||
closeForm()
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
addToast('保存失败:' + error.message, 'error')
|
||||
@@ -464,7 +465,7 @@ export function Channels() {
|
||||
},
|
||||
})
|
||||
|
||||
const resetForm = () => {
|
||||
const resetForm = useCallback(() => {
|
||||
setFormData(createDefaultChannelFormData())
|
||||
setChannelKey('')
|
||||
setJsonValue('')
|
||||
@@ -474,11 +475,30 @@ export function Channels() {
|
||||
setModelEditorMode('visual')
|
||||
setEditingKey(null)
|
||||
setEditMode('form')
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (createMode) {
|
||||
resetForm()
|
||||
setView('form')
|
||||
return
|
||||
}
|
||||
|
||||
setView('list')
|
||||
}, [createMode, resetForm])
|
||||
|
||||
const closeForm = () => {
|
||||
resetForm()
|
||||
setView('list')
|
||||
|
||||
if (createMode) {
|
||||
navigate('/channels', { replace: true })
|
||||
}
|
||||
}
|
||||
|
||||
const handleAdd = () => {
|
||||
resetForm()
|
||||
setView('form')
|
||||
navigate('/channels/new')
|
||||
}
|
||||
|
||||
const handleEdit = (channel: Channel) => {
|
||||
@@ -827,7 +847,7 @@ export function Channels() {
|
||||
<div className="p-4 md:p-6 lg:p-8 animate-in">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<div className="mb-6">
|
||||
<Button variant="ghost" size="sm" className="mb-3 -ml-2 text-muted-foreground" onClick={() => setView('list')}>
|
||||
<Button variant="ghost" size="sm" className="mb-3 -ml-2 text-muted-foreground" onClick={closeForm}>
|
||||
<ArrowLeft className="h-4 w-4 mr-1" />
|
||||
返回列表
|
||||
</Button>
|
||||
@@ -1051,7 +1071,7 @@ export function Channels() {
|
||||
)}
|
||||
|
||||
<div className="flex items-center justify-end gap-3 pt-2">
|
||||
<Button variant="outline" onClick={() => setView('list')}>
|
||||
<Button variant="outline" onClick={closeForm}>
|
||||
取消
|
||||
</Button>
|
||||
<Button onClick={handleSave} disabled={saveMutation.isPending}>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { apiClient } from '@/api/client'
|
||||
import { Token, TokenConfig, Channel } from '@/types'
|
||||
import { Card, CardContent } from '@/components/ui/card'
|
||||
@@ -32,8 +33,9 @@ import { PageContainer } from '@/components/ui/page-container'
|
||||
|
||||
type EditMode = 'form' | 'json'
|
||||
|
||||
export function Tokens() {
|
||||
const [view, setView] = useState<'list' | 'form'>('list')
|
||||
export function Tokens({ createMode = false }: { createMode?: boolean }) {
|
||||
const navigate = useNavigate()
|
||||
const [view, setView] = useState<'list' | 'form'>(createMode ? 'form' : 'list')
|
||||
const [editMode, setEditMode] = useState<EditMode>('form')
|
||||
const [editingKey, setEditingKey] = useState<string | null>(null)
|
||||
const [formData, setFormData] = useState<TokenConfig>({
|
||||
@@ -41,7 +43,7 @@ export function Tokens() {
|
||||
channel_keys: [],
|
||||
total_quota: 0,
|
||||
})
|
||||
const [tokenKey, setTokenKey] = useState('')
|
||||
const [tokenKey, setTokenKey] = useState(() => (createMode ? generateTokenKey() : ''))
|
||||
const [jsonValue, setJsonValue] = useState('')
|
||||
const [availableChannels, setAvailableChannels] = useState<string[]>([])
|
||||
const [selectedChannels, setSelectedChannels] = useState<string[]>([])
|
||||
@@ -86,8 +88,7 @@ export function Tokens() {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['tokens'] })
|
||||
addToast(editingKey ? '令牌更新成功' : '令牌添加成功', 'success')
|
||||
resetForm()
|
||||
setView('list')
|
||||
closeForm()
|
||||
},
|
||||
onError: (error: any) => {
|
||||
addToast('保存失败:' + error.message, 'error')
|
||||
@@ -120,19 +121,38 @@ export function Tokens() {
|
||||
},
|
||||
})
|
||||
|
||||
const resetForm = () => {
|
||||
const resetForm = useCallback(() => {
|
||||
setFormData({ name: '', channel_keys: [], total_quota: 0 })
|
||||
setTokenKey('')
|
||||
setJsonValue('')
|
||||
setSelectedChannels([])
|
||||
setEditingKey(null)
|
||||
setEditMode('form')
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (createMode) {
|
||||
resetForm()
|
||||
setTokenKey(generateTokenKey())
|
||||
setView('form')
|
||||
return
|
||||
}
|
||||
|
||||
setView('list')
|
||||
}, [createMode, resetForm])
|
||||
|
||||
const closeForm = () => {
|
||||
resetForm()
|
||||
setView('list')
|
||||
|
||||
if (createMode) {
|
||||
navigate('/tokens', { replace: true })
|
||||
}
|
||||
}
|
||||
|
||||
const handleAdd = () => {
|
||||
resetForm()
|
||||
setTokenKey(generateTokenKey())
|
||||
setView('form')
|
||||
navigate('/tokens/new')
|
||||
}
|
||||
|
||||
const handleEdit = (token: Token) => {
|
||||
@@ -448,7 +468,7 @@ export function Tokens() {
|
||||
<div className="max-w-2xl mx-auto">
|
||||
{/* Header */}
|
||||
<div className="mb-6">
|
||||
<Button variant="ghost" size="sm" className="mb-3 -ml-2 text-muted-foreground" onClick={() => setView('list')}>
|
||||
<Button variant="ghost" size="sm" className="mb-3 -ml-2 text-muted-foreground" onClick={closeForm}>
|
||||
<ArrowLeft className="h-4 w-4 mr-1" />
|
||||
返回列表
|
||||
</Button>
|
||||
@@ -597,7 +617,7 @@ export function Tokens() {
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex items-center justify-end gap-3 pt-2">
|
||||
<Button variant="outline" onClick={() => setView('list')}>
|
||||
<Button variant="outline" onClick={closeForm}>
|
||||
取消
|
||||
</Button>
|
||||
<Button onClick={handleSave} disabled={saveMutation.isPending}>
|
||||
|
||||
Reference in new issue
Block a user