Compare commits
No commits in common. "4a2cacbd8f22c11e9816949a19a9c6d57e461c59" and "37fca451c150b999bb3969f84ed3a0fcc848d117" have entirely different histories.
4a2cacbd8f
...
37fca451c1
@ -1,228 +0,0 @@
|
||||
import request from '../request'
|
||||
import type { ApiResponse } from '../types'
|
||||
|
||||
// 活动相关的类型定义
|
||||
export interface Activity {
|
||||
id: number
|
||||
title: string
|
||||
subtitle: string
|
||||
description: string
|
||||
startDate: string
|
||||
endDate: string
|
||||
status: 'upcoming' | 'ongoing' | 'ended'
|
||||
registrationCount: number
|
||||
likeCount: number
|
||||
categories: string[]
|
||||
timeline: ActivityTimelineItem[]
|
||||
content: ActivityContentItem[]
|
||||
images: string[]
|
||||
organizer: string
|
||||
coOrganizer?: string
|
||||
}
|
||||
|
||||
export interface ActivityTimelineItem {
|
||||
date: string
|
||||
title: string
|
||||
description: string
|
||||
}
|
||||
|
||||
export interface ActivityContentItem {
|
||||
text: string
|
||||
downloadLink?: string
|
||||
}
|
||||
|
||||
export interface ActivityListItem {
|
||||
id: number
|
||||
title: string
|
||||
subtitle: string
|
||||
courseTitle: string
|
||||
schedule: string
|
||||
duration: string
|
||||
students: string
|
||||
price: string
|
||||
status: string
|
||||
image: string
|
||||
}
|
||||
|
||||
// 活动API类
|
||||
export class ActivityApi {
|
||||
// 获取活动列表
|
||||
static async getActivities(): Promise<ApiResponse<ActivityListItem[]>> {
|
||||
try {
|
||||
// 模拟API调用 - 实际项目中这里应该调用真实的API
|
||||
const mockData: ActivityListItem[] = [
|
||||
{
|
||||
id: 1,
|
||||
title: '计算机二级',
|
||||
subtitle: 'C语言讲练综合班',
|
||||
courseTitle: '计算机二级C语言程序设计证书',
|
||||
schedule: '开课时间:2025.07.26-2025.09.28',
|
||||
duration: '适合年级:高校本科生',
|
||||
students: '已报名:1468/2000',
|
||||
price: '免费',
|
||||
status: '进行中',
|
||||
image: '/images/activity/活动图1.png'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'AI创新实践',
|
||||
subtitle: '全国青少年人工智能创新实践活动',
|
||||
courseTitle: '与AI共创未来 - 2025年全国青少年人工智能创新实践活动',
|
||||
schedule: '开课时间:2025.01.26-2025.09.22',
|
||||
duration: '适合年级:中小学生',
|
||||
students: '已报名:541/1000',
|
||||
price: '免费',
|
||||
status: '报名中',
|
||||
image: '/images/activity/活动图2.png'
|
||||
}
|
||||
]
|
||||
|
||||
return {
|
||||
code: 0,
|
||||
message: '获取活动列表成功',
|
||||
data: mockData
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取活动列表失败:', error)
|
||||
return {
|
||||
code: -1,
|
||||
message: '获取活动列表失败',
|
||||
data: []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 根据ID获取活动详情
|
||||
static async getActivityById(id: number): Promise<ApiResponse<Activity>> {
|
||||
try {
|
||||
// 模拟API调用 - 实际项目中这里应该调用真实的API
|
||||
const mockActivity: Activity = {
|
||||
id: id,
|
||||
title: '"与AI共创未来"',
|
||||
subtitle: '2025年全国青少年人工智能创新实践活动',
|
||||
description: '本活动是面向全国,以青少年为主体的人工智能DeepSeek,该活动以公司自主研发的创新成果,以实践工作作为引导,向青少年群体宣传推广人工智能技术,让青少年了解人工智能技术的发展历程,体验人工智能技术带来的便利,培养青少年对人工智能技术的兴趣,提升青少年的科学素养,为我国人工智能技术的发展培养后备人才。在此基础上,通过实践活动的开展,让青少年在实践中学习,在学习中实践。',
|
||||
startDate: '2025-01-26',
|
||||
endDate: '2025-09-22',
|
||||
status: 'ongoing',
|
||||
registrationCount: 541,
|
||||
likeCount: 2377,
|
||||
categories: [
|
||||
'AI少年明日程',
|
||||
'AI实习程序员',
|
||||
'AI工程实践营',
|
||||
'AI智能未来营',
|
||||
'AI编程教学创新实验'
|
||||
],
|
||||
timeline: [
|
||||
{
|
||||
date: '7月1日-7月24日',
|
||||
title: '青少年编程技能实践活动报名参赛',
|
||||
description: ''
|
||||
},
|
||||
{
|
||||
date: '7月25日-8月31日',
|
||||
title: '青少年编程技能竞赛活动、开发实践活动',
|
||||
description: ''
|
||||
},
|
||||
{
|
||||
date: '8月31日-9月5日',
|
||||
title: '实践作品提交和技能竞赛活动开展',
|
||||
description: ''
|
||||
},
|
||||
{
|
||||
date: '9月6日-9月20日',
|
||||
title: '优秀作品公示期',
|
||||
description: ''
|
||||
},
|
||||
{
|
||||
date: '9月21日-9月27日',
|
||||
title: '人工智能实践活动总结活动举办',
|
||||
description: ''
|
||||
}
|
||||
],
|
||||
content: [
|
||||
{
|
||||
text: '中国科协青少年科技中心、中国青少年科技教育工作者协会等',
|
||||
downloadLink: undefined
|
||||
},
|
||||
{
|
||||
text: '作品提交规则说明',
|
||||
downloadLink: '#'
|
||||
},
|
||||
{
|
||||
text: '中小学教师人工智能素养提升在线学习平台(网址)',
|
||||
downloadLink: '#'
|
||||
},
|
||||
{
|
||||
text: '作品提交 报名表',
|
||||
downloadLink: '#'
|
||||
}
|
||||
],
|
||||
images: [
|
||||
'/images/activity/活动图1.png',
|
||||
'/images/activity/活动图2.png',
|
||||
'/images/activity/活动图3.png'
|
||||
],
|
||||
organizer: '中国科协青少年科技中心',
|
||||
coOrganizer: '中国青少年科技教育工作者协会、上海人工智能实验室'
|
||||
}
|
||||
|
||||
return {
|
||||
code: 0,
|
||||
message: '获取活动详情成功',
|
||||
data: mockActivity
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取活动详情失败:', error)
|
||||
return {
|
||||
code: -1,
|
||||
message: '获取活动详情失败',
|
||||
data: {} as Activity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 报名活动
|
||||
static async registerActivity(activityId: number): Promise<ApiResponse<any>> {
|
||||
try {
|
||||
// 模拟API调用
|
||||
console.log('报名活动:', activityId)
|
||||
|
||||
return {
|
||||
code: 0,
|
||||
message: '报名成功',
|
||||
data: { success: true }
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('报名活动失败:', error)
|
||||
return {
|
||||
code: -1,
|
||||
message: '报名失败',
|
||||
data: { success: false }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 提交作品
|
||||
static async submitWork(activityId: number, workData: any): Promise<ApiResponse<any>> {
|
||||
try {
|
||||
// 模拟API调用
|
||||
console.log('提交作品:', activityId, workData)
|
||||
|
||||
return {
|
||||
code: 0,
|
||||
message: '作品提交成功',
|
||||
data: { success: true }
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('提交作品失败:', error)
|
||||
return {
|
||||
code: -1,
|
||||
message: '作品提交失败',
|
||||
data: { success: false }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ActivityApi
|
@ -17,167 +17,34 @@ export class AuthApi {
|
||||
// 用户登录
|
||||
static async login(data: LoginRequest): Promise<ApiResponse<LoginResponse>> {
|
||||
try {
|
||||
console.log('🚀 发送登录请求:', { url: '/users/login', data: { ...data, password: '***' } })
|
||||
|
||||
// 调用后端API
|
||||
const response = await ApiRequest.post<any>('/users/login', data)
|
||||
const response = await ApiRequest.post<BackendLoginResponse>('/users/login', data)
|
||||
|
||||
console.log('🔍 Login API Response:', response)
|
||||
console.log('🔍 Response Code:', response.code)
|
||||
console.log('🔍 Response Data:', response.data)
|
||||
console.log('🔍 Response Data Type:', typeof response.data)
|
||||
|
||||
// 处理响应格式问题 - 如果code和message是undefined,可能是响应拦截器的问题
|
||||
let actualCode = response.code
|
||||
let actualMessage = response.message
|
||||
let actualData = response.data
|
||||
|
||||
// 如果response.code是undefined,检查response.data是否包含完整的API响应
|
||||
if (actualCode === undefined && actualData && typeof actualData === 'object') {
|
||||
if ('code' in actualData && 'message' in actualData && 'data' in actualData) {
|
||||
// 这种情况下,真正的API响应被包装在了response.data中
|
||||
actualCode = actualData.code
|
||||
actualMessage = actualData.message
|
||||
actualData = actualData.data
|
||||
console.log('🔧 修正后的响应:', { code: actualCode, message: actualMessage, data: actualData })
|
||||
// 适配后端响应格式为前端期望的格式
|
||||
const adaptedResponse: ApiResponse<LoginResponse> = {
|
||||
code: response.code,
|
||||
message: response.message,
|
||||
data: {
|
||||
user: {
|
||||
id: response.data.id, // 使用后端返回的用户ID
|
||||
email: data.email || '',
|
||||
phone: data.phone || '',
|
||||
username: data.phone || data.email?.split('@')[0] || 'user',
|
||||
nickname: '用户',
|
||||
avatar: '',
|
||||
role: 'student',
|
||||
status: 'active',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString()
|
||||
},
|
||||
token: response.data.token,
|
||||
refreshToken: '', // 后端没有返回,使用空字符串
|
||||
expiresIn: 3600 // 默认1小时,可以根据expires字段计算
|
||||
}
|
||||
}
|
||||
|
||||
// 检查响应格式并适配
|
||||
if (actualCode === 200 || actualCode === 0) {
|
||||
// 如果后端返回的是完整的用户信息格式(mock数据格式)
|
||||
if (actualData && actualData.user && actualData.token) {
|
||||
return {
|
||||
code: actualCode,
|
||||
message: actualMessage,
|
||||
data: actualData
|
||||
} as ApiResponse<LoginResponse>
|
||||
}
|
||||
|
||||
// 如果后端返回的是真实API格式(包含token, timestamp, expires)
|
||||
if (actualData && actualData.token && actualData.timestamp) {
|
||||
const adaptedResponse: ApiResponse<LoginResponse> = {
|
||||
code: actualCode,
|
||||
message: actualMessage || '登录成功',
|
||||
data: {
|
||||
user: {
|
||||
id: 1, // 真实API没有返回用户ID,使用默认值
|
||||
email: data.email || '',
|
||||
phone: data.phone || '',
|
||||
username: data.phone || data.email?.split('@')[0] || 'user',
|
||||
nickname: '用户',
|
||||
avatar: '',
|
||||
role: 'student',
|
||||
status: 'active',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString()
|
||||
},
|
||||
token: actualData.token,
|
||||
refreshToken: '', // 真实API没有返回refreshToken
|
||||
expiresIn: 3600 // 默认1小时过期
|
||||
}
|
||||
}
|
||||
return adaptedResponse
|
||||
}
|
||||
|
||||
// 如果后端返回的是简化格式(BackendLoginResponse)
|
||||
if (actualData && actualData.token) {
|
||||
const adaptedResponse: ApiResponse<LoginResponse> = {
|
||||
code: actualCode,
|
||||
message: actualMessage || '登录成功',
|
||||
data: {
|
||||
user: {
|
||||
id: actualData.id || 1,
|
||||
email: data.email || '',
|
||||
phone: data.phone || '',
|
||||
username: data.phone || data.email?.split('@')[0] || 'user',
|
||||
nickname: '用户',
|
||||
avatar: '',
|
||||
role: 'student',
|
||||
status: 'active',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString()
|
||||
},
|
||||
token: actualData.token,
|
||||
refreshToken: actualData.refreshToken || '',
|
||||
expiresIn: actualData.expiresIn || 3600
|
||||
}
|
||||
}
|
||||
return adaptedResponse
|
||||
}
|
||||
|
||||
// 如果只有token字段,直接使用token
|
||||
if (typeof actualData === 'string') {
|
||||
const adaptedResponse: ApiResponse<LoginResponse> = {
|
||||
code: actualCode,
|
||||
message: actualMessage || '登录成功',
|
||||
data: {
|
||||
user: {
|
||||
id: 1,
|
||||
email: data.email || '',
|
||||
phone: data.phone || '',
|
||||
username: data.phone || data.email?.split('@')[0] || 'user',
|
||||
nickname: '用户',
|
||||
avatar: '',
|
||||
role: 'student',
|
||||
status: 'active',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString()
|
||||
},
|
||||
token: actualData,
|
||||
refreshToken: '',
|
||||
expiresIn: 3600
|
||||
}
|
||||
}
|
||||
return adaptedResponse
|
||||
}
|
||||
|
||||
// 如果data是null或undefined,但响应成功,可能是某些API的特殊情况
|
||||
if (!actualData) {
|
||||
console.warn('⚠️ API返回成功但data为空,可能需要检查API实现')
|
||||
throw new Error('登录成功但未返回用户信息')
|
||||
}
|
||||
|
||||
// 尝试处理其他可能的响应格式
|
||||
console.warn('⚠️ 未知的响应格式,尝试通用处理:', actualData)
|
||||
|
||||
// 如果data是对象但不包含预期字段,尝试创建默认响应
|
||||
if (typeof actualData === 'object') {
|
||||
const adaptedResponse: ApiResponse<LoginResponse> = {
|
||||
code: actualCode,
|
||||
message: actualMessage || '登录成功',
|
||||
data: {
|
||||
user: {
|
||||
id: actualData.id || actualData.userId || 1,
|
||||
email: data.email || '',
|
||||
phone: data.phone || '',
|
||||
username: data.phone || data.email?.split('@')[0] || 'user',
|
||||
nickname: actualData.nickname || actualData.name || '用户',
|
||||
avatar: actualData.avatar || '',
|
||||
role: actualData.role || 'student',
|
||||
status: 'active',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString()
|
||||
},
|
||||
token: actualData.accessToken || actualData.access_token || 'temp_token_' + Date.now(),
|
||||
refreshToken: actualData.refreshToken || actualData.refresh_token || '',
|
||||
expiresIn: actualData.expiresIn || actualData.expires_in || 3600
|
||||
}
|
||||
}
|
||||
return adaptedResponse
|
||||
}
|
||||
}
|
||||
|
||||
// 如果响应格式不符合预期,抛出错误
|
||||
console.error('🚨 Unexpected response format:', {
|
||||
code: actualCode,
|
||||
message: actualMessage,
|
||||
data: actualData,
|
||||
dataType: typeof actualData
|
||||
})
|
||||
throw new Error(actualMessage || '登录响应格式错误')
|
||||
return adaptedResponse
|
||||
} catch (error) {
|
||||
console.error('🚨 Login API Error:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@ -189,24 +56,7 @@ export class AuthApi {
|
||||
|
||||
// 用户登出
|
||||
static logout(): Promise<ApiResponse<null>> {
|
||||
// 尝试多个可能的登出端点
|
||||
return ApiRequest.post('/auth/logout').catch(async (error) => {
|
||||
// 如果 /auth/logout 失败,尝试其他可能的端点
|
||||
if (error.response?.status === 404) {
|
||||
try {
|
||||
return await ApiRequest.post('/users/logout')
|
||||
} catch (secondError) {
|
||||
// 如果所有端点都失败,返回成功(客户端登出)
|
||||
console.log('所有登出端点都不存在,执行客户端登出')
|
||||
return {
|
||||
code: 200,
|
||||
message: '登出成功',
|
||||
data: null
|
||||
}
|
||||
}
|
||||
}
|
||||
throw error
|
||||
})
|
||||
return ApiRequest.post('/auth/logout')
|
||||
}
|
||||
|
||||
// 刷新Token
|
||||
|
@ -198,22 +198,13 @@ export class CourseApi {
|
||||
}
|
||||
|
||||
return adaptedResponse
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
console.error('课程API调用失败:', error)
|
||||
|
||||
let errorMessage = '获取课程列表失败'
|
||||
if (error.code === 'ECONNABORTED') {
|
||||
errorMessage = '请求超时,请检查网络连接'
|
||||
} else if (error.message === 'Network Error') {
|
||||
errorMessage = '网络连接失败,请检查网络设置'
|
||||
} else if (error.message?.includes('网络')) {
|
||||
errorMessage = error.message
|
||||
}
|
||||
|
||||
// 返回空数据而不是抛出错误,确保应用不会崩溃
|
||||
return {
|
||||
code: 500,
|
||||
message: errorMessage,
|
||||
message: '获取课程列表失败',
|
||||
data: {
|
||||
list: [],
|
||||
total: 0,
|
||||
|
@ -4,14 +4,6 @@ import { useUserStore } from '@/stores/user'
|
||||
// import router from '@/router'
|
||||
import type { ApiResponse } from './types'
|
||||
|
||||
// 网络状态检测
|
||||
const checkNetworkStatus = (): boolean => {
|
||||
if (typeof navigator !== 'undefined' && 'onLine' in navigator) {
|
||||
return navigator.onLine
|
||||
}
|
||||
return true // 默认认为网络可用
|
||||
}
|
||||
|
||||
// 消息提示函数 - 使用window.alert作为fallback,实际项目中应该使用UI库的消息组件
|
||||
const showMessage = (message: string, type: 'success' | 'error' | 'warning' | 'info' = 'info') => {
|
||||
// 这里可以替换为你使用的UI库的消息组件
|
||||
@ -27,7 +19,7 @@ const showMessage = (message: string, type: 'success' | 'error' | 'warning' | 'i
|
||||
// 创建axios实例
|
||||
const request: AxiosInstance = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000/api',
|
||||
timeout: 30000, // 增加到30秒
|
||||
timeout: 10000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
@ -65,7 +57,7 @@ request.interceptors.request.use(
|
||||
|
||||
// 响应拦截器
|
||||
request.interceptors.response.use(
|
||||
(response: AxiosResponse<any>) => {
|
||||
(response: AxiosResponse<ApiResponse>) => {
|
||||
const { data } = response
|
||||
|
||||
// 开发环境下打印响应信息
|
||||
@ -77,37 +69,20 @@ request.interceptors.response.use(
|
||||
})
|
||||
}
|
||||
|
||||
// 处理不同的响应格式
|
||||
let normalizedData: ApiResponse
|
||||
|
||||
// 如果响应已经是标准格式
|
||||
if (data && typeof data === 'object' && 'code' in data && 'message' in data) {
|
||||
normalizedData = data
|
||||
} else {
|
||||
// 如果响应不是标准格式,包装成标准格式
|
||||
normalizedData = {
|
||||
code: 200,
|
||||
message: '请求成功',
|
||||
data: data
|
||||
}
|
||||
}
|
||||
|
||||
// 检查业务状态码
|
||||
if (normalizedData.code === 200 || normalizedData.code === 0) {
|
||||
// 返回标准化后的响应
|
||||
response.data = normalizedData
|
||||
if (data.code === 200 || data.code === 0) {
|
||||
return response
|
||||
}
|
||||
|
||||
// 处理业务错误
|
||||
const errorMessage = normalizedData.message || '请求失败'
|
||||
const errorMessage = data.message || '请求失败'
|
||||
// 不在这里显示错误消息,让组件自己处理
|
||||
// showMessage(errorMessage, 'error')
|
||||
|
||||
// 创建一个包含完整响应信息的错误对象
|
||||
const error = new Error(errorMessage)
|
||||
;(error as any).response = {
|
||||
data: normalizedData,
|
||||
data: data,
|
||||
status: 200 // HTTP状态码是200,但业务状态码不是成功
|
||||
}
|
||||
return Promise.reject(error)
|
||||
@ -134,13 +109,6 @@ request.interceptors.response.use(
|
||||
errorMessage = '没有权限访问'
|
||||
break
|
||||
case 404:
|
||||
// 对于登出接口的404错误,不显示错误消息
|
||||
if (error.config?.url?.includes('/logout')) {
|
||||
console.log('登出接口不存在,这是正常的')
|
||||
return Promise.resolve({
|
||||
data: { code: 200, message: '登出成功', data: null }
|
||||
})
|
||||
}
|
||||
errorMessage = '请求的资源不存在'
|
||||
break
|
||||
case 422:
|
||||
@ -281,15 +249,6 @@ const handleMockRequest = async <T = any>(url: string, method: string, data?: an
|
||||
} as ApiResponse<T>
|
||||
}
|
||||
|
||||
// 用户登出Mock
|
||||
if (url === '/auth/logout' && method === 'POST') {
|
||||
return {
|
||||
code: 200,
|
||||
message: '登出成功',
|
||||
data: null
|
||||
} as ApiResponse<T>
|
||||
}
|
||||
|
||||
// 课程详情Mock
|
||||
if (url === '/lesson/detail' && method === 'GET') {
|
||||
// 对于GET请求,参数直接在data中(data就是params对象)
|
||||
@ -451,44 +410,10 @@ const handleMockRequest = async <T = any>(url: string, method: string, data?: an
|
||||
} as ApiResponse<T>
|
||||
}
|
||||
|
||||
// 重试机制
|
||||
const retryRequest = async <T = any>(
|
||||
requestFn: () => Promise<T>,
|
||||
maxRetries: number = 2,
|
||||
delay: number = 1000
|
||||
): Promise<T> => {
|
||||
let lastError: any
|
||||
|
||||
for (let i = 0; i <= maxRetries; i++) {
|
||||
try {
|
||||
// 检查网络状态
|
||||
if (!checkNetworkStatus()) {
|
||||
throw new Error('网络连接不可用,请检查网络设置')
|
||||
}
|
||||
|
||||
return await requestFn()
|
||||
} catch (error: any) {
|
||||
lastError = error
|
||||
|
||||
// 如果是最后一次重试,或者不是网络错误,直接抛出
|
||||
if (i === maxRetries || (error.code !== 'ECONNABORTED' && error.message !== 'Network Error' && !error.message?.includes('网络'))) {
|
||||
throw error
|
||||
}
|
||||
|
||||
console.log(`请求失败,${delay}ms后进行第${i + 1}次重试...`)
|
||||
console.log('错误详情:', error.message)
|
||||
await new Promise(resolve => setTimeout(resolve, delay))
|
||||
delay *= 2 // 指数退避
|
||||
}
|
||||
}
|
||||
|
||||
throw lastError
|
||||
}
|
||||
|
||||
// 请求方法封装
|
||||
export class ApiRequest {
|
||||
// GET 请求
|
||||
static async get<T = any>(
|
||||
static get<T = any>(
|
||||
url: string,
|
||||
params?: any,
|
||||
config?: AxiosRequestConfig
|
||||
@ -497,12 +422,11 @@ export class ApiRequest {
|
||||
if (import.meta.env.VITE_ENABLE_MOCK === 'true') {
|
||||
return handleMockRequest<T>(url, 'GET', params)
|
||||
}
|
||||
|
||||
return retryRequest(() => request.get(url, { params, ...config }))
|
||||
return request.get(url, { params, ...config })
|
||||
}
|
||||
|
||||
// POST 请求
|
||||
static async post<T = any>(
|
||||
static post<T = any>(
|
||||
url: string,
|
||||
data?: any,
|
||||
config?: AxiosRequestConfig
|
||||
@ -511,8 +435,7 @@ export class ApiRequest {
|
||||
if (import.meta.env.VITE_ENABLE_MOCK === 'true') {
|
||||
return handleMockRequest<T>(url, 'POST', data)
|
||||
}
|
||||
|
||||
return retryRequest(() => request.post(url, data, config))
|
||||
return request.post(url, data, config)
|
||||
}
|
||||
|
||||
// PUT 请求
|
||||
@ -593,17 +516,6 @@ export class ApiRequest {
|
||||
window.URL.revokeObjectURL(downloadUrl)
|
||||
})
|
||||
}
|
||||
|
||||
// API健康检查
|
||||
static async healthCheck(): Promise<boolean> {
|
||||
try {
|
||||
const response = await request.get('/health', { timeout: 5000 })
|
||||
return response.status === 200
|
||||
} catch (error) {
|
||||
console.warn('API健康检查失败:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default request
|
||||
|
@ -123,8 +123,6 @@ const handleLogin = async () => {
|
||||
|
||||
isLoading.value = true
|
||||
try {
|
||||
console.log('🚀 开始登录:', { account: loginForm.account, password: '***' })
|
||||
|
||||
// 判断输入的是手机号还是邮箱
|
||||
const isPhone = /^[0-9]+$/.test(loginForm.account)
|
||||
|
||||
@ -134,8 +132,6 @@ const handleLogin = async () => {
|
||||
password: loginForm.password
|
||||
})
|
||||
|
||||
console.log('✅ 登录响应:', response)
|
||||
|
||||
if (response.code === 200 || response.code === 0) {
|
||||
const { user, token, refreshToken } = response.data
|
||||
|
||||
@ -145,7 +141,7 @@ const handleLogin = async () => {
|
||||
|
||||
// 保存到本地存储
|
||||
localStorage.setItem('token', token)
|
||||
localStorage.setItem('refreshToken', refreshToken || '')
|
||||
localStorage.setItem('refreshToken', refreshToken)
|
||||
localStorage.setItem('user', JSON.stringify(user))
|
||||
|
||||
// 如果选择了记住我,设置更长的过期时间
|
||||
@ -162,12 +158,11 @@ const handleLogin = async () => {
|
||||
loginForm.password = ''
|
||||
loginForm.remember = false
|
||||
} else {
|
||||
console.error('❌ 登录失败 - 响应码错误:', response)
|
||||
message.error(response.message || '登录失败')
|
||||
}
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('❌ 登录异常:', error)
|
||||
console.error('登录失败:', error)
|
||||
|
||||
// 处理不同类型的错误
|
||||
if (error.response?.status === 401) {
|
||||
|
@ -1,55 +0,0 @@
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
|
||||
export function useNetworkStatus() {
|
||||
const isOnline = ref(navigator.onLine)
|
||||
const isSlowConnection = ref(false)
|
||||
|
||||
const updateOnlineStatus = () => {
|
||||
isOnline.value = navigator.onLine
|
||||
console.log('网络状态变化:', isOnline.value ? '在线' : '离线')
|
||||
}
|
||||
|
||||
const checkConnectionSpeed = async () => {
|
||||
if (!isOnline.value) return
|
||||
|
||||
try {
|
||||
const startTime = Date.now()
|
||||
const response = await fetch('/favicon.ico', {
|
||||
method: 'HEAD',
|
||||
cache: 'no-cache'
|
||||
})
|
||||
const endTime = Date.now()
|
||||
const duration = endTime - startTime
|
||||
|
||||
// 如果请求时间超过3秒,认为是慢连接
|
||||
isSlowConnection.value = duration > 3000
|
||||
console.log('连接速度检测:', duration + 'ms', isSlowConnection.value ? '慢连接' : '正常')
|
||||
} catch (error) {
|
||||
console.warn('连接速度检测失败:', error)
|
||||
isSlowConnection.value = true
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('online', updateOnlineStatus)
|
||||
window.addEventListener('offline', updateOnlineStatus)
|
||||
|
||||
// 初始检测连接速度
|
||||
checkConnectionSpeed()
|
||||
|
||||
// 每30秒检测一次连接速度
|
||||
const speedCheckInterval = setInterval(checkConnectionSpeed, 30000)
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('online', updateOnlineStatus)
|
||||
window.removeEventListener('offline', updateOnlineStatus)
|
||||
clearInterval(speedCheckInterval)
|
||||
})
|
||||
})
|
||||
|
||||
return {
|
||||
isOnline,
|
||||
isSlowConnection,
|
||||
checkConnectionSpeed
|
||||
}
|
||||
}
|
@ -13,7 +13,6 @@ import LearningPaths from '@/views/LearningPaths.vue'
|
||||
import Faculty from '@/views/Faculty.vue'
|
||||
import Resources from '@/views/Resources.vue'
|
||||
import Activities from '@/views/Activities.vue'
|
||||
import ActivityDetail from '@/views/ActivityDetail.vue'
|
||||
import TestSections from '@/views/TestSections.vue'
|
||||
import VideoTest from '@/views/VideoTest.vue'
|
||||
|
||||
@ -102,14 +101,6 @@ const routes: RouteRecordRaw[] = [
|
||||
title: '全部活动'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/activity/:id',
|
||||
name: 'ActivityDetail',
|
||||
component: ActivityDetail,
|
||||
meta: {
|
||||
title: '活动详情'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/test-sections',
|
||||
name: 'TestSections',
|
||||
|
@ -30,21 +30,10 @@ export const useUserStore = defineStore('user', () => {
|
||||
|
||||
const logout = async () => {
|
||||
try {
|
||||
// 尝试调用登出API(如果存在的话)
|
||||
// 但不让API失败阻止登出过程
|
||||
if (token.value) {
|
||||
try {
|
||||
await AuthApi.logout()
|
||||
console.log('服务器端登出成功')
|
||||
} catch (error: any) {
|
||||
// 如果是404错误,说明后端没有登出接口,这是正常的
|
||||
if (error.response?.status === 404) {
|
||||
console.log('后端无登出接口,仅执行客户端登出')
|
||||
} else {
|
||||
console.warn('登出API调用失败,但继续执行客户端登出:', error.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 调用登出API
|
||||
await AuthApi.logout()
|
||||
} catch (error) {
|
||||
console.error('登出API调用失败:', error)
|
||||
} finally {
|
||||
// 无论API调用是否成功,都清除本地数据
|
||||
user.value = null
|
||||
@ -53,7 +42,6 @@ export const useUserStore = defineStore('user', () => {
|
||||
localStorage.removeItem('refreshToken')
|
||||
localStorage.removeItem('user')
|
||||
localStorage.removeItem('rememberMe')
|
||||
console.log('用户已登出')
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,9 +96,6 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
// 加载状态
|
||||
const loading = ref(true)
|
||||
@ -176,8 +173,8 @@ const activities = ref([
|
||||
// 查看详情
|
||||
const viewDetail = (id: number) => {
|
||||
console.log('查看活动详情:', id)
|
||||
// 跳转到活动详情页面
|
||||
router.push(`/activity/${id}`)
|
||||
// 这里可以跳转到活动详情页面
|
||||
// router.push(`/activity/${id}`)
|
||||
}
|
||||
|
||||
// 设置横幅图片的方法(供后续使用)
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user