fix: 修复 CI 构建 49 个 TypeScript 错误
All checks were successful
Deploy Admin Frontend / build-and-deploy (push) Successful in 10s

- routes/index.tsx: DataPages → 独立文件 import
- 导出 CostItem、CostSummary、Conversation、HealthService 类型
- ServerInfo、ProcessInfo、ServerHealth 类型补全嵌套字段
- 修复 useRef 缺初始值、string|null、unused imports 等

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
wangdl 2026-07-01 22:50:22 +08:00
parent 8663ceba66
commit f291f39049
13 changed files with 40 additions and 46 deletions

View File

@ -1,9 +1,10 @@
import { api } from '@/lib/api-client'
export interface CostItem { id: string; currency: string; amount: number; [key: string]: any }
export interface CostItem { id: string; name: string; currency: string; amount: number; purchaseDate?: string; expiryDate?: string; [key: string]: any }
export interface CostSummary { totalMonthly: number; totalYearly: number; totalOneTime: number; items: CostItem[]; byMonth: any[]; expiringSoon: any[] }
export const costsAPI = {
summary() { return api.get<{ totalMonthly: number; byProvider: Record<string, number> }>('/admin-api/costs/summary') },
summary() { return api.get<CostSummary>('/admin-api/costs/summary') },
list() { return api.get<CostItem[]>('/admin-api/costs') },
create(data: Omit<CostItem, 'id' | 'currency'> & { currency?: string }) { return api.post<CostItem>('/admin-api/costs', data) },
update(id: string, data: Partial<CostItem>) { return api.patch<CostItem>(`/admin-api/costs/${id}`, data) },

View File

@ -3,12 +3,12 @@ export { usersAPI } from './users'
export { dashboardAPI } from './dashboard'
export { auditAPI } from './audit'
export { billingAPI, type BillingInfo } from './billing'
export { costsAPI } from './costs'
export { costsAPI, type CostItem, type CostSummary } from './costs'
export { configAPI } from './config'
export { conversationsAPI } from './conversations'
export { conversationsAPI, type Conversation } from './conversations'
export { eventsQueueAPI } from './events-queue'
export { knowledgeAPI } from './knowledge'
export { serversAPI } from './servers'
export { serversAPI, type ServerInfo, type ProcessInfo, type ServerHealth, type HealthService } from './servers'
export { learningAPI } from './learning'
export { aiAPI } from './ai'

View File

@ -1,8 +1,18 @@
import { api } from '@/lib/api-client'
export interface ServerInfo { hostname: string; cpu: number; memory: number; [key: string]: any }
export interface ServerHealth { name: string; status: string; [key: string]: any }
export interface WorkerInfo { name: string; status: string; [key: string]: any }
export interface ServerInfo {
hostname: string
cpu: { usagePercent: number; cores: number; model?: string }
memory: { percent: number; used: string; total: string }
network: { domains: string[]; privateIp?: string; publicIp?: string }
disks: { mount: string; size: string; used: string; percent: number }[]
[key: string]: any
}
export interface ProcessInfo { pid: number; name: string; cpu: number; memory: number; desc?: string; command?: string; [key: string]: any }
export interface HealthService { serviceName: string; status: string; message?: string }
export interface ServerHealth { name: string; status: string; uptime?: string; pid?: number; services?: HealthService[]; queues?: string[]; [key: string]: any }
export interface WorkerInfo { name: string; status: string; queues?: string[]; [key: string]: any }
export const serversAPI = {
metrics() { return api.get<{ servers: ServerInfo[] }>('/admin-api/servers') },

View File

@ -2,7 +2,7 @@ import { useState } from 'react'
import { useQuery, useQueryClient } from '@tanstack/react-query'
import { Card, Row, Col, Statistic, Button, Tag, Space, Typography, App, Table, Modal, Form, Input, Select, DatePicker, InputNumber, Tabs } from 'antd'
import { DollarOutlined, ReloadOutlined, LinkOutlined, PlusOutlined, DeleteOutlined, EditOutlined, CloudOutlined, DownloadOutlined } from '@ant-design/icons'
import { billingAPI, costsAPI, type BillingInfo } from '@/api'
import { billingAPI, costsAPI, type BillingInfo, type CostItem, type CostSummary } from '@/api'
import dayjs from 'dayjs'
const { Text, Title } = Typography

View File

@ -30,7 +30,7 @@ export default function ImportMonitorPage() {
// 知识库远程搜索(防抖)
const [kbSearch, setKbSearch] = useState('')
const kbSearchTimer = useRef<ReturnType<typeof setTimeout>>()
const kbSearchTimer = useRef<ReturnType<typeof setTimeout>>(undefined)
const handleKbSearch = useCallback((val: string) => {
clearTimeout(kbSearchTimer.current)

View File

@ -30,7 +30,7 @@ function KBPage() {
const [refSourceId, setRefSourceId] = useState<string | null>(null)
const { data: references } = useQuery({
queryKey: ['source-refs', refSourceId],
queryFn: (): Promise<any> => knowledgeAPI.sourceReferences(refSourceId),
queryFn: (): Promise<any> => knowledgeAPI.sourceReferences(refSourceId || ''),
enabled: !!refSourceId,
})

View File

@ -26,23 +26,6 @@ interface KnowledgeSourceItem {
kbName?: string
}
interface PaginatedSources {
items: KnowledgeSourceItem[]
total: number
page: number
limit: number
}
interface KnowledgeBaseItem {
id: string
title?: string
name?: string
}
interface PaginatedKnowledgeBases {
items: KnowledgeBaseItem[]
}
// ── Labels ──
const mimeLabels: Record<string, string> = {

View File

@ -24,7 +24,7 @@ export default function ReportingAdmin() {
const download = async (type: string) => {
try {
const text = await reportingAPI.exportCSV(type, days)
const text = await reportingAPI.exportCSV(type, String(days))
downloadBlob(text as string, `${type}-report-${days}d.csv`)
} catch {
message.error('导出失败')

View File

@ -2,7 +2,7 @@ import { useState } from 'react'
import { useQuery, useQueryClient } from '@tanstack/react-query'
import { Card, Row, Col, Progress, Table, Tag, Typography, Button, Space, Tooltip, App } from 'antd'
import { CloudServerOutlined, ReloadOutlined, CopyOutlined, GlobalOutlined } from '@ant-design/icons'
import { serversAPI, type ServerInfo, type ServerHealth } from '@/api/servers'
import { serversAPI, type ServerInfo, type ProcessInfo, type ServerHealth } from '@/api/servers'
const { Text, Title } = Typography
@ -28,8 +28,8 @@ function ServerCard({ server }: { server: ServerInfo }) {
{/* IP & domains — fixed height */}
<div style={{ marginBottom: 12, height: 54, overflow: 'hidden' }}>
<Space wrap size={[4, 4]}>
<CopyTag text={server.network.publicIp} icon="🌐 " color="cyan" />
<CopyTag text={server.network.privateIp} icon="🔒 " />
<CopyTag text={server.network.publicIp || ''} icon="🌐 " color="cyan" />
<CopyTag text={server.network.privateIp || ''} icon="🔒 " />
{server.network.domains.map(d => (
<CopyTag key={d} text={d} icon={<GlobalOutlined style={{ marginRight: 2 }} />} color="blue" />
))}
@ -186,7 +186,7 @@ function ServersContent() {
{(health as ServerHealth[]).map(h => (
<Card key={h.serverName} title={h.serverName} size="small" style={{ marginBottom: 8 }}>
<Space wrap>
{h.services.map(s => (
{(h.services ?? []).map(s => (
<Tag key={s.serviceName} color={s.status === 'healthy' ? 'green' : 'red'}>
{s.serviceName} · {s.message}
</Tag>

View File

@ -1,7 +1,7 @@
import { useState, useRef, useEffect, useCallback } from 'react'
import { Input, Button, Typography, App, message as antMsg } from 'antd'
import { PlusOutlined, ToolOutlined, DeleteOutlined, MessageOutlined, ArrowUpOutlined, CopyOutlined } from '@ant-design/icons'
import { aiAPI, conversationsAPI } from '@/api'
import { aiAPI, conversationsAPI, type Conversation } from '@/api'
import { streamChat, type StreamEvent } from '@/services/ai-chat'
import Markdown from '@/components/Markdown'
@ -37,7 +37,7 @@ function ChatPage() {
const switchConversation = useCallback(async (id: string) => {
if (streaming) { abortRef.current?.abort(); setStreaming(false); setWaitingApproval(false) }
setActiveId(id); setMessages([])
try { const records = await conversationsAPI.messages(id); setMessages(records.map(m => ({ id: m.id, role: m.role, content: m.content, timestamp: new Date(m.createdAt).getTime() }))) } catch {}
try { const records = await conversationsAPI.messages(id); setMessages(records.map(m => ({ id: m.id, role: m.role as 'user' | 'assistant', content: m.content, timestamp: new Date(m.createdAt).getTime() }))) } catch {}
}, [streaming])
const handleNew = async () => {

View File

@ -1,5 +1,5 @@
import { useState } from 'react';
import { Table, Input, Select, Space, Button, Drawer, Descriptions, DatePicker } from 'antd';
import { Table, Input, Space, Button, Drawer, Descriptions, DatePicker } from 'antd';
import { useQuery } from '@tanstack/react-query';
import { learningAPI } from '@/api';
import { PageWrapper } from './PageWrapper';

View File

@ -1,5 +1,5 @@
import { useState } from 'react';
import { Table, Input, Select, Space, Tag, Button, Tabs, Drawer, Descriptions, DatePicker, Timeline, Typography, Popconfirm } from 'antd';
import { Table, Input, Select, Space, Tag, Button, Drawer, Descriptions, Popconfirm } from 'antd';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { learningAPI } from '@/api';
import { sessionStatusLabels, targetTypeLabels, sessionModeLabels } from '@/constants/labels';

View File

@ -6,15 +6,15 @@ const UserManagement = lazy(() => import('@/pages/UserManagement'))
const MemberManagement = lazy(() => import('@/pages/MemberManagement'))
const LearningDashboard = lazy(() => import('@/pages/learning/Dashboard'))
const ReadingEventPage = lazy(() => import('@/pages/learning/DataPages').then(m => ({ default: m.ReadingEventPage })))
const SessionPage = lazy(() => import('@/pages/learning/DataPages').then(m => ({ default: m.SessionPage })))
const ProgressPage = lazy(() => import('@/pages/learning/DataPages').then(m => ({ default: m.ProgressPage })))
const DailyActivityPage = lazy(() => import('@/pages/learning/DataPages').then(m => ({ default: m.DailyActivityPage })))
const RecordPage = lazy(() => import('@/pages/learning/DataPages').then(m => ({ default: m.RecordPage })))
const AnomalyPage = lazy(() => import('@/pages/learning/DataPages').then(m => ({ default: m.AnomalyPage })))
const UserTimelinePage = lazy(() => import('@/pages/learning/DataPages').then(m => ({ default: m.UserTimelinePage })))
const UserDiagnosePage = lazy(() => import('@/pages/learning/DataPages').then(m => ({ default: m.UserDiagnosePage })))
const MaterialDiagnosePage = lazy(() => import('@/pages/learning/DataPages').then(m => ({ default: m.MaterialDiagnosePage })))
const ReadingEventPage = lazy(() => import('@/pages/learning/ReadingEventPage'))
const SessionPage = lazy(() => import('@/pages/learning/SessionPage'))
const ProgressPage = lazy(() => import('@/pages/learning/ProgressPage'))
const DailyActivityPage = lazy(() => import('@/pages/learning/DailyActivityPage'))
const RecordPage = lazy(() => import('@/pages/learning/RecordPage'))
const AnomalyPage = lazy(() => import('@/pages/learning/AnomalyPage'))
const UserTimelinePage = lazy(() => import('@/pages/learning/UserTimelinePage'))
const UserDiagnosePage = lazy(() => import('@/pages/learning/UserDiagnosePage'))
const MaterialDiagnosePage = lazy(() => import('@/pages/learning/MaterialDiagnosePage'))
const ServersPage = lazy(() => import('@/pages/Servers'))
const KnowledgeSourcesPage = lazy(() => import('@/pages/KnowledgeSources'))
const LearningReplay = lazy(() => import('@/pages/learning/ReplayPage'))