Nextfox文档
一、项目介绍
Nextfox是一个高效简洁易用的测试管理工具,包含:项目管理、系统配置、功能测试、接口测试、UI自动化测试、性能测试、精准测试、便捷工具等实用功能。
1、前端技术
- 前端开发:Next + React+Antd + TypeScript + TailwindCSS
2、功能模块
二、环境搭建
1、相关依赖官网
- 前端中文学习网址:https://nodejs.cn/
- 页面元素文档:https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element
- 元素属性文档:https://developer.mozilla.org/zh-CN/docs/Web/HTML/Attributes
- Node.js 官网:https://nodejs.org/en
- Next.js 官网:https://nextjs.org/
- react.js 官网:https://react.dev/
- Jsdelivr 官网:https://www.jsdelivr.com/
- Babel 官网:https://www.babeljs.cn/
- antd 官网:https://ant-design.antgroup.com/docs/react/use-with-next-cn
- Tailwind 官网:https://tailwindcss.com/
- clsx 官网:https://www.npmjs.com/package/clsx
- React-icons 图标官网:https://react-icons.github.io/react-icons/
- avatar 官网:https://pravatar.cc/
- Zustand 官网:https://zustand-demo.pmnd.rs/
- icons图标免费网站:
2、前端环境搭建
创建 Next.js 项目
设置镜像加速器
npm config set registry https://registry.npmmirror.com首先,确保你已经安装了 Node.js 和 npm 或 yarn。然后在终端中运行以下命令创建一个新的 Next.js 项目,并启用 TypeScript:
npx create-next-app@latest nextfox-frontend安装必要的依赖项
安装 Ant Design、postcss、autoprefixer及其所需的其他依赖项:
npm install antd tailwindcss postcss autoprefixer运行开发服务器
在项目根目录中运行以下命令启动开发服务器:
pnpm i # 安装项目依赖 pnpm dev # 启动本地服务 // pnpm dev 你可以在浏览器中访问 http://localhost:3000 查看项目
3、前端项目结构
- 创建项目目录
mkdir -p src/{apis,app,components,contexts,enums,hooks,styles,types,utils}
- pulbic:静态资源目录
- images目录:存放图片
- favicon.svg:svg图片
- manifest.webmanifest:应用元数据
- src:源文件夹
- apis:请求接口
- app:页面组件
- components:公共组件
- contexts:上下文管理
- hooks:自定义钩子
- enums:定义枚举类型
- types:定义TypeScript类型
- styles:全局样式
- utils:工具函数
- next.config.mjs: nextjs 配置
- .env.development:开发环境变量
- .env.production:发布版本环境变量
- tsconfig.json:TypeScript 项目的配置文件
- postcss.config.mjs:css样式插件
- tailwind.config.ts: tailwindcss 全局设置
- Dockerfile: docker 构建配置
- .eslinttrc.cjs: eslint配置信息
- .gitignore: git忽略文件
- .prettierrc.cjs: 代码格式设置
- .stylelintrc.cjs:Stylelint 配置文件
- commitlint.config: commit 提交设置
- next-env.d.ts:提供 Next.js 特定的类型声明
4、配置项
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
async rewrites() {
return [
{
source: '/api/:path*',
destination: `${process.env.NEXT_PUBLIC_API_URL}/:path*`
},
]
}
};
export default nextConfig;
NEXT_PUBLIC_API_URL=http://localhost:5173/api
NEXT_PUBLIC_API_URL=
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
tailwindcss: {},
autoprefixer: {},
'tailwindcss/nesting': {},
},
};
export default config;
import type { Config } from 'tailwindcss'
export default {
content: ['./src/{app,components}/**/*.{js,jsx,ts,tsx}'],
theme: {
extend: {
colors: {},
padding: {
main: 'var(--p-main)',
tabContent: 'var(--p-tab-content)',
},
margin: {
tabContent: 'var(--p-tab-content)',
},
},
},
corePlugins: {
preflight: false,
},
} satisfies Config
// 安装commitlint
npm add -D @commitlint/config-conventional @commitlint/cli
四、app页面组件
page.tsx:项目主页面
import { redirect } from "next/navigation";
export default function HomePage() {
return redirect('/login')
}
layout.tsx:项目主模版
app/layout.tsx 是整个应用的主布局文件,相当于React的main.ts或App.tsx,做以下几个事情:
- 项目 metadata
- 加载全局样式 globals.css
- 加载网络/本地字体
- 国际化 i18n
- 第三方组件库 Provider Wrapper
- 顶层 RootLayout 作用于所有页面,各个子 Layout 只作用于自己所属的目录下的所有页面
import type { Metadata, Viewport } from 'next';
import { Inter } from "next/font/google";
import '@/styles/globals.css';
import { getPageTitle } from '@/utils/getPageTitle';
import { App } from 'antd';
import { AntdRegistry } from '@ant-design/nextjs-registry'
import { AntdStyleProvider } from '@/components/AntdStyleProvider';
import { ThemeProviderClient } from '@/components/ThemeEditor';
import { GlobalContextProvider } from '@/contexts/GlobalContext';
export const metadata: Metadata = {
icons: [{ url: '/favicon.svg', type: 'image/svg+xml' }],
title: getPageTitle(),
description: 'Nextfox--高效简洁易用的测试管理工具',
authors: [{ name: 'TestCabana', url: 'https://github.com/TestCabana' }],
manifest: '/manifest.webmanifest',
};
const inter = Inter({ subsets: ["latin"] });
export const viewport: Viewport = {
colorScheme: 'light dark',
};
export default function RootLayout(props: React.PropsWithChildren) {
return (
{props.children}
)
}
not-found.tsx:页面不存在
'use client';
import { Button, Result } from 'antd';
import { useRouter } from 'next/navigation';
export default function NotFound() {
const router = useRouter();
return (
回到主页}
extra={
}
/>
);
}
(main)/layout.tsx:页面主模板
'use client'
import '@/styles/globals.css';
import { theme } from 'antd'
import { SideNav } from '@/components/SideNav'
import { LayoutProvider } from '@/contexts/LayoutContext'
import { HeaderNav } from '@/components/HeaderNav/HeaderNav'
import { useThemeContext } from '@/contexts/ThemeContext';
export default function MainLayout(props: React.PropsWithChildren) {
const { themeSetting } = useThemeContext();
const { themeMode } = themeSetting;
const { token } = theme.useToken()
// 根据不同的主题模式设置背景颜色
const backgroundColor = themeMode === 'darkDefault'
? token.colorFillTertiary
: 'var(--background-color)';
// 根据不同的主题模式设置文字颜色
const colorText = themeMode === 'darkDefault'
? 'var(--colorText)'
: token.colorText;
return (
{props.children}
)
}
1、user
1)LoginForm.tsx:登录表单
'use client'; // 声明这个页面组件为客户端组件
import React, { useState, useEffect } from 'react';
import { Button, Input, Form, message } from 'antd';
import { UserOutlined, LockOutlined } from '@ant-design/icons';
import Image from 'next/image';
import useLoading from '@/hooks/useLoading';
import { useRouter } from 'next/navigation';
import styles from './login.module.css'; // 导入 CSS 模块
import { setLocalStorage } from '@/utils/localStorage';
import { encrypted } from '@/utils/encrypted';
import { getPublicKey, login } from '@/apis/user/user';
import { Response } from '@/types';
export default function LoginForm() {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [publicKey, setPublicKey] = useState('');
const { loading, setLoading } = useLoading();
const router = useRouter();
useEffect(() => {
// 获取公钥
const getPublicKeyReq = async () => {
try {
const response = await getPublicKey();
const publicKey = response.data; // 明确指定类型
console.log('公钥', publicKey)
setPublicKey(publicKey || null); // 设置公钥状态
setLocalStorage('salt', publicKey); // 将公钥存储到本地存储
} catch (error) {
console.error('获取公钥失败:', error);
}
};
getPublicKeyReq();
}, []); // 依赖数组为空,确保只在组件首次加载时调用
const handleLogin = async (username: string, password: string) => {
if (!publicKey) {
console.error('未获取公钥');
return false;
}
try {
// 使用公钥加密用户名和密码
const encryptedUsername = encrypted(username);
const encryptedPassword = encrypted(password);
if (encryptedUsername === false || encryptedPassword === false) {
console.error('加密失败');
return false;
}
const data = {
username: encryptedUsername,
password: encryptedPassword,
};
const responseData = await login(data);
// 登录成功后将用户信息、sessionId、token存储在LocalStorage中
setLocalStorage('user', responseData);
setLocalStorage('sessionId', responseData.data.sessionId)
setLocalStorage('csrfToken', responseData.data.csrfToken)
// 登录成功后重定向到 /dashboard
router.push('/dashboard');
return true;
} catch (error) {
console.error('登录请求失败:', error);
return false;
}
};
const handleSubmit = async (values: { username: string; password: string }) => {
setLoading(true);
try {
const success = await handleLogin(values.username, values.password);
if (!success) {
message.error('登录请求失败');
}
} catch (error) {
message.error('登录时发生错误');
} finally {
setLoading(false);
}
};
return (
Nextfox
高效简洁易用的开源测试管理工具
账号登录
}
value={username}
onChange={(e) => setUsername(e.target.value)}
allowClear
/>
}
value={password}
onChange={(e) => setPassword(e.target.value)}
allowClear
/>
);
}
2)page.ts:登录页面
import LoginForm from './LoginForm';
import styles from './login.module.css';
export default function LoginPage() {
return (
);
}
2)page.tsx:登录页面
import LoginForm from './LoginForm';
import styles from './login.module.css';
export default function LoginPage() {
return (
);
}
3)login.module.css:登录样式
/* login.module.css */
.backgroundLogin {
background-image: url('/images/login-background.jpg');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
width: 100vw;
}
/* 容器样式 */
.container {
@apply flex items-center justify-center m-2;
}
/* 登录表单包装样式 */
.loginFormWrapper {
@apply flex flex-col items-center justify-center;
}
/* 标题样式 */
.title {
@apply flex items-center justify-center;
.titleText {
@apply text-[35px] font-bold text-[#9373ee];
}
}
.subtitle {
@apply mt-3 text-[18px] font-bold text-[#9373ee];
}
/* 表单容器样式 */
.formContainer {
@apply mt-8 p-10 rounded-lg bg-white shadow-2xl;
.formTitle {
@apply mb-8 text-[18px] font-bold text-[#9373ee];
}
.formItem {
@apply mb-8;
}
.submitButton {
@apply w-full flex justify-center;
}
}
2、system-settings
1)page.tsx
'use client'
// 导入 Ant Design 组件库中的 `ConfigProvider`(配置提供者),`Menu`(菜单),以及 `Skeleton`(加载占位符)组件。
import { ConfigProvider, Menu, type MenuProps, Skeleton, theme } from 'antd'
import { LayersIcon, SettingsIcon } from 'lucide-react'
import { PanelLayout } from '@/components/PanelLayout'
// 定义 `MenuItem` 类型,它是 `MenuProps` 中 `items` 属性的类型。`Required` 强制要求 `MenuProps` 中的所有属性都必须被指定。
type MenuItem = Required['items'][number]
// 定义一个 `items` 数组,包含了菜单项的数据结构
const items: MenuItem[] = [
{
key: 'g1', // 菜单项的唯一标识符
label: (
通用设置
),
type: 'group', // 表示这是一个分组项
children: [ // 子菜单项
{ key: '1', label: '基本设置' }, // 第一个子菜单项
{
key: '2', // 第二个子菜单项的标识符
label: '功能设置', // 子菜单项的文本标签
children: [ // 子菜单项下的子菜单项
{ key: '1x', label: '接口功能设置' }, // 子菜单项
{ key: '2x', label: '高级设置' }, // 子菜单项
],
},
],
},
{
key: 'g2', // 另一个分组菜单项的标识符
label: (
项目资源
),
type: 'group', // 表示这是一个分组项
children: [
{ key: '3', label: '常用参数' },
{ key: '4', label: '公共响应' },
],
},
]
export default function SettingsPage() {
const { token } = theme.useToken()
return (