diff --git a/src/App.tsx b/src/App.tsx
index d9a8f8c..ef6bd3d 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -12,7 +12,7 @@ import AdminLayout from './layouts/AdminLayout'
const Login = lazy(() => import('./pages/Login'))
const KnowledgeBasesPage = lazy(() => import('./pages/KnowledgeBases'))
const BillingPage = lazy(() => import('./pages/Billing'))
-const GiteaEmbed = lazy(() => import('./pages/GiteaEmbed'))
+const ProjectCenter = lazy(() => import('./pages/GiteaEmbed'))
const ConfigPage = lazy(() => import("./pages/Config"))
const SecurityEventsPage = lazy(() => import('./pages/SecurityEvents'))
const ThrottlePage = lazy(() => import('./pages/Throttle'))
@@ -131,7 +131,7 @@ function App() {
}>
+ }>
}
/>
, requiredRole: 'ADMIN' },
{ path: '/audit', name: '审计日志', icon: , requiredRole: 'ADMIN' },
{ path: '/billing', name: 'API 用量', icon: , requiredRole: 'SUPER_ADMIN' },
- { path: '/git', name: '代码仓库', icon: },
+ { path: '/git', name: '项目中心', icon: },
{ path: '/ops', name: '系统运维', icon: , requiredRole: 'SUPER_ADMIN', children: [
{ path: '/throttle', name: '限流管理' },
{ path: '/security-events', name: '安全事件' },
diff --git a/src/pages/GiteaEmbed.tsx b/src/pages/GiteaEmbed.tsx
index d3bb4c6..802f1e8 100644
--- a/src/pages/GiteaEmbed.tsx
+++ b/src/pages/GiteaEmbed.tsx
@@ -1,10 +1,137 @@
-export default function GiteaEmbed() {
+import { useState } from 'react'
+import { Table, Tag, Tabs, Typography, Card, Row, Col, Spin } from 'antd'
+import { CodeOutlined, BranchesOutlined, RocketOutlined } from '@ant-design/icons'
+import { useQuery } from '@tanstack/react-query'
+import { api } from '@/services/http-client'
+
+const { Title, Text } = Typography
+
+export default function ProjectCenter() {
+ const [selectedRepo, setSelectedRepo] = useState(null)
+ const [tab, setTab] = useState('repos')
+
+ const { data: repos, isLoading } = useQuery({
+ queryKey: ['gitea', 'repos'],
+ queryFn: () => api.get('/admin-api/projects/repos').then(d => d ?? []),
+ })
+
+ const repoParts = selectedRepo?.split('/') || []
+
+ const { data: issues } = useQuery({
+ queryKey: ['gitea', 'issues', selectedRepo],
+ queryFn: () => api.get(`/admin-api/projects/repos/${repoParts[0]}/${repoParts[1]}/issues?state=all`).then(d => d ?? []),
+ enabled: tab === 'issues' && repoParts.length === 2,
+ })
+
+ const { data: milestones } = useQuery({
+ queryKey: ['gitea', 'milestones', selectedRepo],
+ queryFn: () => api.get(`/admin-api/projects/repos/${repoParts[0]}/${repoParts[1]}/milestones`).then(d => d ?? []),
+ enabled: tab === 'milestones' && repoParts.length === 2,
+ })
+
+ const { data: releases } = useQuery({
+ queryKey: ['gitea', 'releases', selectedRepo],
+ queryFn: () => api.get(`/admin-api/projects/repos/${repoParts[0]}/${repoParts[1]}/releases`).then(d => d ?? []),
+ enabled: tab === 'releases' && repoParts.length === 2,
+ })
+
+ const { data: runners } = useQuery({
+ queryKey: ['gitea', 'runners'],
+ queryFn: () => api.get('/admin-api/projects/runners').then(d => d ?? []),
+ enabled: tab === 'runners',
+ })
+
+ const repoColumns = [
+ { title: '仓库', dataIndex: 'fullName', width: 200, render: (n: string, r: any) => { setSelectedRepo(n); setTab('issues') }}>{n} },
+ { title: '描述', dataIndex: 'description', width: 200, ellipsis: true, render: (d: string) => d || '-' },
+ { title: 'Issues', dataIndex: 'openIssues', width: 70 },
+ { title: 'PRs', dataIndex: 'openPulls', width: 70 },
+ { title: '里程碑', dataIndex: 'milestones', width: 70 },
+ { title: 'Stars', dataIndex: 'stars', width: 60 },
+ { title: '分支', dataIndex: 'defaultBranch', width: 80, render: (b: string) => {b} },
+ { title: '更新', dataIndex: 'updatedAt', width: 100, render: (d: string) => d ? new Date(d).toLocaleDateString() : '-' },
+ ]
+
+ const issueColumns = [
+ { title: '#', dataIndex: 'number', width: 60 },
+ { title: '标题', dataIndex: 'title', width: 300, ellipsis: true },
+ { title: '状态', dataIndex: 'state', width: 70, render: (s: string) => {s} },
+ { title: '创建者', dataIndex: ['user', 'login'], width: 100 },
+ { title: '更新', dataIndex: 'updated_at', width: 100, render: (d: string) => d ? new Date(d).toLocaleDateString() : '-' },
+ ]
+
return (
-