feat: 完成所有页面的演示模式实现
- 更新 DashboardLayout 组件,统一使用演示模式布局 - 实现仪表盘页面的完整演示数据和功能 - 完成用户管理页面的演示模式,包含搜索、过滤、分页等功能 - 实现通话记录页面的演示数据和录音播放功能 - 完成翻译员管理页面的演示模式 - 实现订单管理页面的完整功能 - 完成发票管理页面的演示数据 - 更新文档管理页面 - 添加 utils.ts 工具函数库 - 完善 API 路由和数据库结构 - 修复各种 TypeScript 类型错误 - 统一界面风格和用户体验
This commit is contained in:
@@ -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}`
|
||||
: '服务器内部错误'
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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')
|
||||
}
|
||||
}
|
||||
@@ -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 : '未知错误'}`
|
||||
: '服务器内部错误'
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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 : '未知错误'}`
|
||||
: '服务器内部错误'
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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')
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user