feat: 完成所有页面的演示模式实现

- 更新 DashboardLayout 组件,统一使用演示模式布局
- 实现仪表盘页面的完整演示数据和功能
- 完成用户管理页面的演示模式,包含搜索、过滤、分页等功能
- 实现通话记录页面的演示数据和录音播放功能
- 完成翻译员管理页面的演示模式
- 实现订单管理页面的完整功能
- 完成发票管理页面的演示数据
- 更新文档管理页面
- 添加 utils.ts 工具函数库
- 完善 API 路由和数据库结构
- 修复各种 TypeScript 类型错误
- 统一界面风格和用户体验
This commit is contained in:
2025-06-30 19:42:43 +08:00
parent 0b8be9377a
commit f20988b90c
36 changed files with 8752 additions and 3638 deletions
+102
View File
@@ -0,0 +1,102 @@
import { NextApiRequest, NextApiResponse } from 'next';
import jwt from 'jsonwebtoken';
interface LoginRequest {
email: string;
password: string;
}
// 硬编码的管理员凭据(用于演示)
const ADMIN_CREDENTIALS = {
email: 'admin@example.com',
password: 'admin123',
user: {
id: 'admin-001',
email: 'admin@example.com',
name: '系统管理员',
userType: 'admin',
phone: '13800138000',
avatarUrl: null
}
};
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'POST') {
return res.status(405).json({ success: false, error: '方法不允许' });
}
try {
const { email, password }: LoginRequest = req.body;
console.log('收到登录请求:', { email, password: '***' });
// 验证必填字段
if (!email || !password) {
console.log('缺少必填字段');
return res.status(400).json({
success: false,
error: '邮箱和密码不能为空'
});
}
// 验证邮箱格式
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
console.log('邮箱格式不正确:', email);
return res.status(400).json({
success: false,
error: '邮箱格式不正确'
});
}
console.log('验证管理员凭据...');
// 验证管理员凭据
if (email !== ADMIN_CREDENTIALS.email || password !== ADMIN_CREDENTIALS.password) {
console.log('管理员凭据不正确');
return res.status(401).json({
success: false,
error: '邮箱或密码错误'
});
}
console.log('管理员凭据验证通过');
// 生成JWT令牌
const jwtSecret = process.env.JWT_SECRET || 'your-secret-key';
const token = jwt.sign(
{
userId: ADMIN_CREDENTIALS.user.id,
email: ADMIN_CREDENTIALS.user.email,
userType: ADMIN_CREDENTIALS.user.userType,
name: ADMIN_CREDENTIALS.user.name
},
jwtSecret,
{ expiresIn: '24h' }
);
console.log('JWT令牌生成成功');
console.log('登录成功,返回用户信息');
// 返回成功响应
res.status(200).json({
success: true,
message: '登录成功',
user: ADMIN_CREDENTIALS.user,
token,
expiresIn: '24h'
});
} catch (error) {
console.error('登录错误:', error);
res.status(500).json({
success: false,
error: process.env.NODE_ENV === 'development'
? `服务器错误: ${error}`
: '服务器内部错误'
});
}
}
+92
View File
@@ -0,0 +1,92 @@
import { NextApiRequest, NextApiResponse } from 'next'
import { auth } from '../../../lib/supabase'
import { getUserProfile, handleApiError, validateEmail } from '../../../lib/api-utils'
interface LoginRequest {
email: string
password: string
}
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'POST') {
return res.status(405).json({ success: false, error: '方法不允许' })
}
try {
const { email, password }: LoginRequest = req.body
// 验证必填字段
if (!email || !password) {
return res.status(400).json({
success: false,
error: '缺少必填字段',
details: '邮箱和密码为必填项'
})
}
// 验证邮箱格式
if (!validateEmail(email)) {
return res.status(400).json({
success: false,
error: '邮箱格式不正确'
})
}
// 登录用户
const authData = await auth.signIn(email, password)
if (!authData?.user || !authData?.session) {
return res.status(401).json({
success: false,
error: '登录失败,请检查邮箱和密码'
})
}
// 获取用户详细信息
const userProfile = await getUserProfile(authData.user.id)
// 更新最后登录时间
if (userProfile) {
try {
await auth.updateUser({
user_metadata: {
...authData.user.user_metadata,
last_login: new Date().toISOString()
}
})
} catch (updateError) {
console.error('Update last login error:', updateError)
// 不影响登录流程
}
}
return res.status(200).json({
success: true,
message: '登录成功',
data: {
user: userProfile || {
id: authData.user.id,
email: authData.user.email,
name: authData.user.user_metadata?.name || '',
user_type: authData.user.user_metadata?.user_type || 'individual',
enterprise_id: authData.user.user_metadata?.enterprise_id || null,
status: 'active',
phone: authData.user.user_metadata?.phone || null,
created_at: authData.user.created_at,
updated_at: authData.user.updated_at
},
session: {
access_token: authData.session.access_token,
refresh_token: authData.session.refresh_token,
expires_at: authData.session.expires_at
}
}
})
} catch (error) {
return handleApiError(res, error, 'Login')
}
}
+47
View File
@@ -0,0 +1,47 @@
import type { NextApiRequest, NextApiResponse } from 'next'
import { supabase } from '../../../lib/supabase'
interface ApiResponse {
success: boolean
message?: string
error?: string
}
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<ApiResponse>
) {
if (req.method !== 'POST') {
return res.status(405).json({
success: false,
error: '方法不允许'
})
}
try {
// 从Supabase登出
const { error } = await supabase.auth.signOut()
if (error) {
console.error('Logout error:', error)
return res.status(400).json({
success: false,
error: error.message || '登出失败'
})
}
return res.status(200).json({
success: true,
message: '登出成功'
})
} catch (error) {
console.error('Server error during logout:', error)
return res.status(500).json({
success: false,
error: process.env.NODE_ENV === 'development'
? `服务器错误: ${error instanceof Error ? error.message : '未知错误'}`
: '服务器内部错误'
})
}
}
+85
View File
@@ -0,0 +1,85 @@
import type { NextApiRequest, NextApiResponse } from 'next'
import { supabase } from '../../../lib/supabase'
interface UserInfo {
id: string
email: string
name: string
phone?: string
user_type: 'individual' | 'enterprise' | 'admin'
status: 'active' | 'inactive' | 'suspended'
enterprise_id?: string
avatar_url?: string
created_at: string
updated_at: string
}
interface ApiResponse {
success: boolean
data?: UserInfo
error?: string
}
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<ApiResponse>
) {
if (req.method !== 'GET') {
return res.status(405).json({
success: false,
error: '方法不允许'
})
}
try {
// 获取授权头
const authHeader = req.headers.authorization
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({
success: false,
error: '未提供有效的授权令牌'
})
}
const token = authHeader.substring(7) // 移除 'Bearer ' 前缀
// 验证JWT令牌
const { data: { user }, error: authError } = await supabase.auth.getUser(token)
if (authError || !user) {
return res.status(401).json({
success: false,
error: '无效的授权令牌'
})
}
// 从数据库获取用户详细信息
const { data: userProfile, error: profileError } = await supabase
.from('users')
.select('*')
.eq('id', user.id)
.single()
if (profileError) {
console.error('Error fetching user profile:', profileError)
return res.status(404).json({
success: false,
error: '用户信息不存在'
})
}
return res.status(200).json({
success: true,
data: userProfile
})
} catch (error) {
console.error('Server error getting user info:', error)
return res.status(500).json({
success: false,
error: process.env.NODE_ENV === 'development'
? `服务器错误: ${error instanceof Error ? error.message : '未知错误'}`
: '服务器内部错误'
})
}
}
+117
View File
@@ -0,0 +1,117 @@
import { NextApiRequest, NextApiResponse } from 'next'
import { auth, db } from '../../../lib/supabase'
import { handleApiError, validateEmail, validatePassword } from '../../../lib/api-utils'
interface RegisterRequest {
email: string
password: string
name: string
phone?: string
user_type: 'individual' | 'enterprise'
enterprise_id?: string
}
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'POST') {
return res.status(405).json({ success: false, error: '方法不允许' })
}
try {
const { email, password, name, phone, user_type, enterprise_id }: RegisterRequest = req.body
// 验证必填字段
if (!email || !password || !name || !user_type) {
return res.status(400).json({
success: false,
error: '缺少必填字段',
details: '邮箱、密码、姓名和用户类型为必填项'
})
}
// 验证邮箱格式
if (!validateEmail(email)) {
return res.status(400).json({
success: false,
error: '邮箱格式不正确'
})
}
// 验证密码强度
const passwordValidation = validatePassword(password)
if (!passwordValidation.valid) {
return res.status(400).json({
success: false,
error: passwordValidation.message
})
}
// 检查邮箱是否已注册
try {
const existingUsers = await db.select('users', '*')
const existingUser = existingUsers.find((user: any) => user.email === email)
if (existingUser) {
return res.status(400).json({
success: false,
error: '该邮箱已被注册'
})
}
} catch (error) {
console.error('Check existing user error:', error)
// 继续注册流程,让Supabase处理重复邮箱的情况
}
// 注册用户
const authData = await auth.signUp(email, password, {
name,
phone,
user_type,
enterprise_id
})
if (!authData?.user) {
return res.status(400).json({
success: false,
error: '注册失败,请稍后重试'
})
}
// 创建用户记录
try {
const userData = {
id: authData.user.id,
email,
name,
phone: phone || null,
user_type,
enterprise_id: enterprise_id || null,
status: 'active',
created_at: new Date().toISOString(),
updated_at: new Date().toISOString()
}
const userRecord = await db.insert('users', userData)
return res.status(201).json({
success: true,
message: '注册成功',
data: {
user: userRecord,
needEmailVerification: !authData.session // 如果没有session,说明需要邮箱验证
}
})
} catch (dbError) {
console.error('Create user record error:', dbError)
return res.status(500).json({
success: false,
error: '用户注册成功,但创建用户记录失败',
details: process.env.NODE_ENV === 'development' ? (dbError as Error).message : undefined
})
}
} catch (error) {
return handleApiError(res, error, 'Register')
}
}
+125
View File
@@ -0,0 +1,125 @@
import { NextApiRequest, NextApiResponse } from 'next'
import { getDemoData } from '../../../lib/demo-data'
import {
authenticateUser,
getUserProfile,
handleApiError,
generateOrderNumber,
calculateServiceCost,
User
} from '../../../lib/api-utils'
import { db } from '../../../lib/supabase'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
// 认证用户
const user = await authenticateUser(req, res)
if (!user) return // 错误已在authenticateUser中处理
if (req.method === 'GET') {
// 获取订单列表
try {
// 在演示模式下返回演示数据
if (process.env.NODE_ENV === 'development' && !process.env.SUPABASE_URL) {
const demoOrders = await getDemoData.orders()
return res.status(200).json({
success: true,
data: {
orders: demoOrders,
total: demoOrders.length
}
})
}
// 从数据库获取订单
const orders = await db.select('orders', '*')
// 根据用户类型过滤订单
const currentUser = await getUserProfile(user.id)
let filteredOrders = orders
if (currentUser && currentUser.user_type === 'individual') {
// 个人用户只能看到自己的订单
filteredOrders = orders.filter((order: any) => order.user_id === user.id)
}
// 企业用户和管理员可以看到所有订单
return res.status(200).json({
success: true,
data: {
orders: filteredOrders,
total: filteredOrders.length
}
})
} catch (error) {
return handleApiError(res, error, 'Get orders')
}
} else if (req.method === 'POST') {
// 创建新订单
const {
service_type,
service_name,
source_language,
target_language,
duration,
priority = 'normal',
scheduled_time,
notes
} = req.body
// 验证必填字段
if (!service_type || !source_language || !target_language) {
return res.status(400).json({
success: false,
error: '缺少必填字段',
details: '服务类型、源语言和目标语言为必填项'
})
}
try {
// 获取用户信息
const currentUser = await getUserProfile(user.id)
const orderData = {
order_number: generateOrderNumber(),
user_id: user.id,
user_name: currentUser?.name || user.email,
user_email: user.email,
service_type,
service_name: service_name || service_type,
source_language,
target_language,
duration: duration || null,
status: 'pending',
priority,
cost: calculateServiceCost(service_type, duration),
currency: 'CNY',
scheduled_time: scheduled_time || null,
notes: notes || null,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString()
}
const newOrder = await db.insert('orders', orderData)
return res.status(201).json({
success: true,
message: '订单创建成功',
data: {
order: newOrder
}
})
} catch (error) {
return handleApiError(res, error, 'Create order')
}
} else {
return res.status(405).json({
success: false,
error: '方法不允许'
})
}
}
+55
View File
@@ -0,0 +1,55 @@
import { NextApiRequest, NextApiResponse } from 'next';
import { supabase, isSupabaseConfigured } from '../../lib/supabase';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'GET') {
return res.status(405).json({ error: '方法不允许' });
}
try {
// 检查配置
const isConfigured = isSupabaseConfigured();
if (!isConfigured) {
return res.status(200).json({
success: false,
mode: 'demo',
message: '当前运行在演示模式,未配置 Supabase 数据库'
});
}
// 测试数据库连接
const { data, error } = await supabase
.from('users')
.select('count')
.limit(1);
if (error) {
console.error('数据库连接错误:', error);
return res.status(500).json({
success: false,
mode: 'production',
error: error.message,
message: '数据库连接失败'
});
}
return res.status(200).json({
success: true,
mode: 'production',
message: '数据库连接成功',
timestamp: new Date().toISOString()
});
} catch (error: any) {
console.error('连接测试失败:', error);
return res.status(500).json({
success: false,
error: error.message,
message: '连接测试失败'
});
}
}