[重构] 前端改造v1

This commit is contained in:
ryan
2026-03-11 12:47:08 +08:00
parent 052e3e98f8
commit 6eea676f8f
71 changed files with 3619 additions and 8791 deletions
+40
View File
@@ -0,0 +1,40 @@
import { publicEnv } from '@/lib/env/public-env';
import type { ApiEnvelope } from '@/types/api';
export class ApiError extends Error {
constructor(
message: string,
public readonly status: number,
) {
super(message);
this.name = 'ApiError';
}
}
export function getApiUrl(path: string) {
const normalizedPath = path.startsWith('/') ? path : `/${path}`;
return `${publicEnv.apiBaseUrl}${normalizedPath}`;
}
export async function apiRequest<T>(path: string, init?: RequestInit) {
const response = await fetch(getApiUrl(path), {
credentials: 'include',
headers: {
'Content-Type': 'application/json',
...(init?.headers ?? {}),
},
...init,
});
if (!response.ok) {
throw new ApiError(`请求失败(${response.status})`, response.status);
}
const payload = (await response.json()) as ApiEnvelope<T>;
if (!payload.success) {
throw new ApiError(payload.message || '请求失败', response.status);
}
return payload.data;
}
@@ -0,0 +1,64 @@
import type { NavigationItem } from '@/types/navigation';
export const dashboardNavigation: NavigationItem[] = [
{
href: '/',
label: '总览',
shortLabel: '总览',
description: '查看阶段推进情况、系统入口与模块骨架状态。',
},
{
href: '/proxy-routes',
label: '反代规则',
shortLabel: '规则',
description: '阶段 3 接入列表、表单与发布动作。',
},
{
href: '/config-versions',
label: '配置版本',
shortLabel: '版本',
description: '阶段 3 接入版本预览、激活与回滚体验。',
},
{
href: '/nodes',
label: '节点管理',
shortLabel: '节点',
description: '阶段 3 接入状态标签、部署命令与更新动作。',
},
{
href: '/apply-logs',
label: '应用记录',
shortLabel: '记录',
description: '阶段 3 接入筛选、分页与详情展示。',
},
{
href: '/managed-domains',
label: '域名管理',
shortLabel: '域名',
description: '阶段 3 接入证书绑定与启用状态切换。',
},
{
href: '/tls-certificates',
label: 'TLS 证书',
shortLabel: '证书',
description: '阶段 3 接入导入、上传与有效期展示。',
},
{
href: '/files',
label: '文件管理',
shortLabel: '文件',
description: '阶段 4 接入列表、下载与管理动作。',
},
{
href: '/users',
label: '用户管理',
shortLabel: '用户',
description: '阶段 4 接入用户列表、角色与搜索体验。',
},
{
href: '/settings',
label: '设置',
shortLabel: '设置',
description: '阶段 4 接入系统设置、运维设置与个人设置。',
},
];
+26
View File
@@ -0,0 +1,26 @@
import { z } from 'zod';
const publicEnvSchema = z.object({
NEXT_PUBLIC_API_BASE_URL: z.string().default('/api'),
NEXT_PUBLIC_APP_VERSION: z.string().default('dev'),
});
const rawEnv = {
NEXT_PUBLIC_API_BASE_URL: process.env.NEXT_PUBLIC_API_BASE_URL,
NEXT_PUBLIC_APP_VERSION: process.env.NEXT_PUBLIC_APP_VERSION,
};
const parsedEnv = publicEnvSchema.parse(rawEnv);
function normalizeBaseUrl(value: string) {
if (value === '/') {
return '';
}
return value.replace(/\/+$/, '');
}
export const publicEnv = {
apiBaseUrl: normalizeBaseUrl(parsedEnv.NEXT_PUBLIC_API_BASE_URL),
appVersion: parsedEnv.NEXT_PUBLIC_APP_VERSION,
};
+6
View File
@@ -0,0 +1,6 @@
import { type ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
+14
View File
@@ -0,0 +1,14 @@
import { dashboardNavigation } from '@/lib/constants/navigation';
import type { NavigationItem } from '@/types/navigation';
export function isPathActive(pathname: string, href: string) {
if (href === '/') {
return pathname === '/';
}
return pathname === href || pathname.startsWith(`${href}/`);
}
export function getCurrentNavigationItem(pathname: string): NavigationItem | undefined {
return dashboardNavigation.find((item) => isPathActive(pathname, item.href));
}