From 6db5d43891eccd55df797fa462e5b69f80d15d2c Mon Sep 17 00:00:00 2001 From: shenlan Date: Wed, 18 Jun 2025 10:51:38 +0800 Subject: [PATCH] Add Next.js admin panel skeleton --- docs/ui/nextjs-admin-panel.md | 3 +++ ui/nextjs/app/agent/page.tsx | 10 ++++++++++ ui/nextjs/app/api/page.tsx | 10 ++++++++++ ui/nextjs/app/layout.tsx | 15 +++++++++++++++ ui/nextjs/app/page.tsx | 10 ++++++++++ ui/nextjs/app/subscription/page.tsx | 10 ++++++++++ ui/nextjs/app/xray/page.tsx | 10 ++++++++++ ui/nextjs/components/Card.tsx | 7 +++++++ ui/nextjs/components/Header.tsx | 7 +++++++ ui/nextjs/components/Sidebar.tsx | 21 +++++++++++++++++++++ ui/nextjs/lib/api.ts | 5 +++++ ui/nextjs/next-env.d.ts | 4 ++++ ui/nextjs/next.config.mjs | 3 +++ ui/nextjs/postcss.config.js | 6 ++++++ ui/nextjs/styles/globals.css | 7 +++++++ ui/nextjs/tailwind.config.js | 11 +++++++++++ ui/nextjs/tsconfig.json | 24 ++++++++++++++++++++++++ 17 files changed, 163 insertions(+) create mode 100644 ui/nextjs/app/agent/page.tsx create mode 100644 ui/nextjs/app/api/page.tsx create mode 100644 ui/nextjs/app/layout.tsx create mode 100644 ui/nextjs/app/page.tsx create mode 100644 ui/nextjs/app/subscription/page.tsx create mode 100644 ui/nextjs/app/xray/page.tsx create mode 100644 ui/nextjs/components/Card.tsx create mode 100644 ui/nextjs/components/Header.tsx create mode 100644 ui/nextjs/components/Sidebar.tsx create mode 100644 ui/nextjs/lib/api.ts create mode 100644 ui/nextjs/next-env.d.ts create mode 100644 ui/nextjs/next.config.mjs create mode 100644 ui/nextjs/postcss.config.js create mode 100644 ui/nextjs/styles/globals.css create mode 100644 ui/nextjs/tailwind.config.js create mode 100644 ui/nextjs/tsconfig.json diff --git a/docs/ui/nextjs-admin-panel.md b/docs/ui/nextjs-admin-panel.md index a47f252..3401c06 100644 --- a/docs/ui/nextjs-admin-panel.md +++ b/docs/ui/nextjs-admin-panel.md @@ -46,3 +46,6 @@ styles/ ``` 生成的 `out/` 目录即可作为纯静态网站部署。 + +## 示例代码结构 +本仓库 `ui/nextjs` 目录提供了一套最简参考实现,可直接 `npm run build && npx next export` 编译到 `ui/dist` 供 Go 服务嵌入。 diff --git a/ui/nextjs/app/agent/page.tsx b/ui/nextjs/app/agent/page.tsx new file mode 100644 index 0000000..eeddc5c --- /dev/null +++ b/ui/nextjs/app/agent/page.tsx @@ -0,0 +1,10 @@ +export const dynamic = 'force-static' + +export default function AgentPage() { + return ( +
+

Agent Management

+

Manage node agents here.

+
+ ) +} diff --git a/ui/nextjs/app/api/page.tsx b/ui/nextjs/app/api/page.tsx new file mode 100644 index 0000000..25d6502 --- /dev/null +++ b/ui/nextjs/app/api/page.tsx @@ -0,0 +1,10 @@ +export const dynamic = 'force-static' + +export default function APIPage() { + return ( +
+

API Status

+

View backend API health here.

+
+ ) +} diff --git a/ui/nextjs/app/layout.tsx b/ui/nextjs/app/layout.tsx new file mode 100644 index 0000000..6487a5e --- /dev/null +++ b/ui/nextjs/app/layout.tsx @@ -0,0 +1,15 @@ +import '../styles/globals.css' +import Sidebar from '../components/Sidebar' + +export const dynamic = 'force-static' + +export default function RootLayout({ children }: { children: React.ReactNode }) { + return ( + + + +
{children}
+ + + ) +} diff --git a/ui/nextjs/app/page.tsx b/ui/nextjs/app/page.tsx new file mode 100644 index 0000000..01e8650 --- /dev/null +++ b/ui/nextjs/app/page.tsx @@ -0,0 +1,10 @@ +export const dynamic = 'force-static' + +export default function Home() { + return ( +
+

Dashboard

+

Welcome to XControl admin panel.

+
+ ) +} diff --git a/ui/nextjs/app/subscription/page.tsx b/ui/nextjs/app/subscription/page.tsx new file mode 100644 index 0000000..271699e --- /dev/null +++ b/ui/nextjs/app/subscription/page.tsx @@ -0,0 +1,10 @@ +export const dynamic = 'force-static' + +export default function SubscriptionPage() { + return ( +
+

Subscription

+

Manage vless subscriptions.

+
+ ) +} diff --git a/ui/nextjs/app/xray/page.tsx b/ui/nextjs/app/xray/page.tsx new file mode 100644 index 0000000..b286c1c --- /dev/null +++ b/ui/nextjs/app/xray/page.tsx @@ -0,0 +1,10 @@ +export const dynamic = 'force-static' + +export default function XRayPage() { + return ( +
+

XRay

+

Control XRay configuration.

+
+ ) +} diff --git a/ui/nextjs/components/Card.tsx b/ui/nextjs/components/Card.tsx new file mode 100644 index 0000000..a3cab47 --- /dev/null +++ b/ui/nextjs/components/Card.tsx @@ -0,0 +1,7 @@ +export default function Card({ children }: { children: React.ReactNode }) { + return ( +
+ {children} +
+ ) +} diff --git a/ui/nextjs/components/Header.tsx b/ui/nextjs/components/Header.tsx new file mode 100644 index 0000000..940bcde --- /dev/null +++ b/ui/nextjs/components/Header.tsx @@ -0,0 +1,7 @@ +export default function Header() { + return ( +
+

XControl Admin

+
+ ) +} diff --git a/ui/nextjs/components/Sidebar.tsx b/ui/nextjs/components/Sidebar.tsx new file mode 100644 index 0000000..132d6e9 --- /dev/null +++ b/ui/nextjs/components/Sidebar.tsx @@ -0,0 +1,21 @@ +import Link from 'next/link' + +const menu = [ + { href: '/', label: 'Home' }, + { href: '/agent', label: 'Agent' }, + { href: '/api', label: 'API' }, + { href: '/subscription', label: 'Subscription' }, + { href: '/xray', label: 'XRay' }, +] + +export default function Sidebar() { + return ( + + ) +} diff --git a/ui/nextjs/lib/api.ts b/ui/nextjs/lib/api.ts new file mode 100644 index 0000000..dc013ae --- /dev/null +++ b/ui/nextjs/lib/api.ts @@ -0,0 +1,5 @@ +export async function fetchJSON(url: string): Promise { + const res = await fetch(url) + if (!res.ok) throw new Error(`API error: ${res.status}`) + return res.json() as Promise +} diff --git a/ui/nextjs/next-env.d.ts b/ui/nextjs/next-env.d.ts new file mode 100644 index 0000000..84ab714 --- /dev/null +++ b/ui/nextjs/next-env.d.ts @@ -0,0 +1,4 @@ +/// +/// + +// NOTE: This file should not be edited diff --git a/ui/nextjs/next.config.mjs b/ui/nextjs/next.config.mjs new file mode 100644 index 0000000..dba2ea7 --- /dev/null +++ b/ui/nextjs/next.config.mjs @@ -0,0 +1,3 @@ +export default { + output: 'export', +} diff --git a/ui/nextjs/postcss.config.js b/ui/nextjs/postcss.config.js new file mode 100644 index 0000000..33ad091 --- /dev/null +++ b/ui/nextjs/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/ui/nextjs/styles/globals.css b/ui/nextjs/styles/globals.css new file mode 100644 index 0000000..a3ff494 --- /dev/null +++ b/ui/nextjs/styles/globals.css @@ -0,0 +1,7 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +body { + @apply text-gray-800; +} diff --git a/ui/nextjs/tailwind.config.js b/ui/nextjs/tailwind.config.js new file mode 100644 index 0000000..b86de8d --- /dev/null +++ b/ui/nextjs/tailwind.config.js @@ -0,0 +1,11 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + './app/**/*.{ts,tsx}', + './components/**/*.{ts,tsx}', + ], + theme: { + extend: {}, + }, + plugins: [], +} diff --git a/ui/nextjs/tsconfig.json b/ui/nextjs/tsconfig.json new file mode 100644 index 0000000..ac6383d --- /dev/null +++ b/ui/nextjs/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": false, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "paths": { + "@/*": ["./*"] + }, + "types": ["node"] + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +}