"添加前端模板和运行代码模块"

This commit is contained in:
Codex
2026-06-01 13:43:09 +08:00
parent 24045274ad
commit 8092c4b1f8
587 changed files with 99761 additions and 0 deletions

View File

@@ -0,0 +1,106 @@
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios';
interface ApiResponse<T = any> {
code: number;
data: T;
message: string;
}
interface ApiError {
code: number;
message: string;
}
class ApiClient {
private instance: AxiosInstance;
constructor(baseURL: string = '/api') {
this.instance = axios.create({
baseURL,
timeout: 10000,
headers: {
'Content-Type': 'application/json',
},
});
this.setupInterceptors();
}
private setupInterceptors() {
// 请求拦截器
this.instance.interceptors.request.use(
(config) => {
// 可以在这里添加token等认证信息
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
// 响应拦截器
this.instance.interceptors.response.use(
(response: AxiosResponse<ApiResponse>) => {
const { code, data, message } = response.data;
// 处理业务错误
if (code !== 200) {
const error: ApiError = { code, message };
return Promise.reject(error);
}
return data;
},
(error: AxiosError<ApiError>) => {
if (error.response) {
// 服务器返回错误状态码
const { status, data } = error.response;
console.error('API Error:', status, data?.message || error.message);
} else if (error.request) {
// 请求已经发出,但没有收到响应
console.error('Network Error:', error.message);
} else {
// 在设置请求时发生错误
console.error('Request Error:', error.message);
}
return Promise.reject(error);
}
);
}
async get<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
return this.instance.get(url, config);
}
async post<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
return this.instance.post(url, data, config);
}
async put<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
return this.instance.put(url, data, config);
}
async delete<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
return this.instance.delete(url, config);
}
async patch<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
return this.instance.patch(url, data, config);
}
}
// 便捷方法
export const api = {
get: <T>(url: string, config?: AxiosRequestConfig) => new ApiClient().get<T>(url, config),
post: <T>(url: string, data?: any, config?: AxiosRequestConfig) => new ApiClient().post<T>(url, data, config),
put: <T>(url: string, data?: any, config?: AxiosRequestConfig) => new ApiClient().put<T>(url, data, config),
delete: <T>(url: string, config?: AxiosRequestConfig) => new ApiClient().delete<T>(url, config),
patch: <T>(url: string, data?: any, config?: AxiosRequestConfig) => new ApiClient().patch<T>(url, data, config),
};
export default ApiClient;

View File

@@ -0,0 +1,134 @@
import { type ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
/**
* 合并 Tailwind CSS 类名的工具函数
* @param inputs - 类名数组或对象
* @returns 合并后的类名字符串
*/
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
/**
* 格式化日期
* @param date - 日期对象或字符串
* @param options - 格式化选项
* @returns 格式化后的日期字符串
*/
export function formatDate(
date: Date | string,
options: Intl.DateTimeFormatOptions = {}
): string {
const dateObj = typeof date === 'string' ? new Date(date) : date;
return new Intl.DateTimeFormat('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric',
...options,
}).format(dateObj);
}
/**
* 格式化数字
* @param num - 数字
* @param options - 格式化选项
* @returns 格式化后的数字字符串
*/
export function formatNumber(
num: number,
options: Intl.NumberFormatOptions = {}
): string {
return new Intl.NumberFormat('zh-CN', options).format(num);
}
/**
* 生成随机 ID
* @param length - ID 长度
* @returns 随机 ID 字符串
*/
export function generateId(length: number = 8): string {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}
/**
* 防抖函数
* @param func - 要防抖的函数
* @param wait - 等待时间(毫秒)
* @returns 防抖后的函数
*/
export function debounce<T extends (...args: any[]) => any>(
func: T,
wait: number
): (...args: Parameters<T>) => void {
let timeout: NodeJS.Timeout;
return (...args: Parameters<T>) => {
clearTimeout(timeout);
timeout = setTimeout(() => func(...args), wait);
};
}
/**
* 节流函数
* @param func - 要节流的函数
* @param limit - 时间限制(毫秒)
* @returns 节流后的函数
*/
export function throttle<T extends (...args: any[]) => any>(
func: T,
limit: number
): (...args: Parameters<T>) => void {
let inThrottle: boolean;
return (...args: Parameters<T>) => {
if (!inThrottle) {
func(...args);
inThrottle = true;
setTimeout(() => (inThrottle = false), limit);
}
};
}
/**
* 检查是否为移动设备
* @returns 是否为移动设备
*/
export function isMobile(): boolean {
if (typeof window === 'undefined') return false;
return window.innerWidth < 768;
}
/**
* 检查是否为暗色模式
* @returns 是否为暗色模式
*/
export function isDarkMode(): boolean {
if (typeof window === 'undefined') return false;
return document.documentElement.classList.contains('dark');
}
/**
* 切换暗色模式
*/
export function toggleDarkMode(): void {
if (typeof window === 'undefined') return;
document.documentElement.classList.toggle('dark');
localStorage.setItem('darkMode', document.documentElement.classList.contains('dark').toString());
}
/**
* 初始化暗色模式
*/
export function initDarkMode(): void {
if (typeof window === 'undefined') return;
const saved = localStorage.getItem('darkMode');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (saved === 'true' || (saved === null && prefersDark)) {
document.documentElement.classList.add('dark');
}
}