后台
This commit is contained in:
parent
7d77a11dee
commit
86c7e52551
53
src/api/axiosConfig.js
Normal file
53
src/api/axiosConfig.js
Normal file
@ -0,0 +1,53 @@
|
||||
import axios from 'axios';
|
||||
import { logoutUser } from '../utils/jwt'; // logoutUser会处理清除存储和重定向
|
||||
|
||||
const API_BASE_URL = 'http://zybdatasupport.online:8000';
|
||||
|
||||
const axiosInstance = axios.create({
|
||||
baseURL: API_BASE_URL,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
},
|
||||
timeout: 10000
|
||||
});
|
||||
|
||||
// 请求拦截器
|
||||
axiosInstance.interceptors.request.use(
|
||||
config => {
|
||||
const token = localStorage.getItem('access_token');
|
||||
const url = config.url;
|
||||
|
||||
// 定义不需要Token的接口条件
|
||||
const noAuthRequired =
|
||||
url === '/user/login' ||
|
||||
url === '/user/register' || // 明确添加注册接口
|
||||
url.endsWith('/getlist');
|
||||
|
||||
if (token && !noAuthRequired) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
error => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// 响应拦截器
|
||||
axiosInstance.interceptors.response.use(
|
||||
response => response,
|
||||
error => {
|
||||
const originalRequest = error.config;
|
||||
|
||||
// 如果收到401错误,并且不是来自登录请求本身
|
||||
if (error.response && error.response.status === 401 && originalRequest.url !== '/user/login') {
|
||||
logoutUser(); // 调用简化的logoutUser,它只清除token,不导航
|
||||
}
|
||||
// 不需要额外的console.error,错误会自然地在调用处被捕获或显示在网络请求中
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export default axiosInstance;
|
@ -1,50 +1,4 @@
|
||||
import axios from 'axios';
|
||||
|
||||
const API_BASE_URL = 'http://zybdatasupport.online:8000'; // 与 tournament.js 一致
|
||||
|
||||
// 创建 axios 实例,可以复用 tournament.js 中的拦截器逻辑,或者单独设置
|
||||
const axiosInstance = axios.create({
|
||||
baseURL: API_BASE_URL,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
},
|
||||
timeout: 10000
|
||||
});
|
||||
|
||||
// 请求拦截器 (添加token)
|
||||
axiosInstance.interceptors.request.use(
|
||||
config => {
|
||||
const token = localStorage.getItem('access_token');
|
||||
if (token) {
|
||||
config.headers.Authorization = `bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
error => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// 响应拦截器 (基本错误处理)
|
||||
axiosInstance.interceptors.response.use(
|
||||
response => response,
|
||||
error => {
|
||||
if (error.response) {
|
||||
console.error('API请求错误 (demands.js):', {
|
||||
status: error.response.status,
|
||||
data: error.response.data,
|
||||
config: error.config
|
||||
});
|
||||
} else if (error.request) {
|
||||
console.error('网络错误 (demands.js):', error.request);
|
||||
} else {
|
||||
console.error('请求配置错误 (demands.js):', error.message);
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
import axiosInstance from './axiosConfig';
|
||||
|
||||
/**
|
||||
* 获取需求列表
|
||||
@ -94,18 +48,9 @@ export const addDemand = async (demandData) => {
|
||||
*/
|
||||
export const updateDemand = async (id, dataToUpdate) => {
|
||||
try {
|
||||
// 根据后端PUT /demands/update/{id} 的定义,它期望整个DemandModel作为item
|
||||
// 但只更新了 sendcontent。为安全起见,先获取原始数据,再更新。
|
||||
// 或者,如果API设计为只接受要修改的字段,则直接发送 { sendcontent: dataToUpdate.sendcontent }
|
||||
// 这里假设API会处理部分更新,或者前端会发送完整的模型(即使只改一个字段)
|
||||
// 为简单起见,我们先假设API能接受只包含sendcontent的更新,或者前端需要先获取完整模型再提交
|
||||
// 参照后端 item: DemandModel, 它会接收一个完整的模型,但仅使用了 item.sendcontent
|
||||
// 因此,我们需要传递一个至少包含 sendcontent 的对象,但为了模型验证,最好是完整的模型结构
|
||||
const payload = {
|
||||
sendcontent: dataToUpdate.sendcontent,
|
||||
// 根据 DemandModel,补齐其他必填或可选字段,即使它们不被后端 update 逻辑使用
|
||||
// 这部分需要参照 DemandModel 的具体定义来决定哪些字段是必要的
|
||||
// 以下为推测,需要根据实际 DemandModel 调整
|
||||
requester: dataToUpdate.requester || '',
|
||||
qq_code: dataToUpdate.qq_code || '',
|
||||
content: dataToUpdate.sendcontent, // 保持一致
|
||||
|
133
src/api/login.js
133
src/api/login.js
@ -1,80 +1,75 @@
|
||||
import axios from 'axios'
|
||||
import axiosInstance from './axiosConfig';
|
||||
import { loginSuccess } from '../utils/jwt';
|
||||
|
||||
const API_BASE_URL = 'http://zybdatasupport.online:8000'
|
||||
// const API_BASE_URL = 'http://zybdatasupport.online:8000' // 不再需要
|
||||
|
||||
// 创建 axios 实例
|
||||
const axiosInstance = axios.create({
|
||||
baseURL: API_BASE_URL,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
},
|
||||
timeout: 10000 // 添加超时设置
|
||||
})
|
||||
// // 创建 axios 实例 // 不再需要
|
||||
// const axiosInstance = axios.create({
|
||||
// baseURL: API_BASE_URL,
|
||||
// headers: {
|
||||
// 'Content-Type': 'application/json',
|
||||
// 'Accept': 'application/json',
|
||||
// 'X-Requested-With': 'XMLHttpRequest'
|
||||
// },
|
||||
// timeout: 10000 // 添加超时设置
|
||||
// })
|
||||
|
||||
// 设置请求拦截器,自动添加 token
|
||||
axiosInstance.interceptors.request.use(
|
||||
config => {
|
||||
const token = localStorage.getItem('access_token')
|
||||
if (token) {
|
||||
config.headers.Authorization = `bearer ${token}`
|
||||
}
|
||||
return config
|
||||
},
|
||||
error => {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
// // 设置请求拦截器,自动添加 token // 不再需要
|
||||
// axiosInstance.interceptors.request.use(
|
||||
// config => {
|
||||
// const token = localStorage.getItem('access_token')
|
||||
// if (token) {
|
||||
// config.headers.Authorization = `bearer ${token}`
|
||||
// }
|
||||
// return config
|
||||
// },
|
||||
// error => {
|
||||
// return Promise.reject(error)
|
||||
// }
|
||||
// )
|
||||
|
||||
// 添加响应拦截器
|
||||
axiosInstance.interceptors.response.use(
|
||||
response => response,
|
||||
error => {
|
||||
if (error.response) {
|
||||
// 服务器返回错误状态码
|
||||
console.error('请求错误:', {
|
||||
status: error.response.status,
|
||||
data: error.response.data,
|
||||
config: error.config
|
||||
})
|
||||
} else if (error.request) {
|
||||
// 请求已发出但没有收到响应
|
||||
console.error('网络错误:', error.request)
|
||||
} else {
|
||||
// 请求配置出错
|
||||
console.error('请求配置错误:', error.message)
|
||||
}
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
// // 添加响应拦截器 // 不再需要
|
||||
// axiosInstance.interceptors.response.use(
|
||||
// response => response,
|
||||
// error => {
|
||||
// if (error.response) {
|
||||
// // 服务器返回错误状态码
|
||||
// console.error('请求错误:', {
|
||||
// status: error.response.status,
|
||||
// data: error.response.data,
|
||||
// config: error.config
|
||||
// })
|
||||
// } else if (error.request) {
|
||||
// // 请求已发出但没有收到响应
|
||||
// console.error('网络错误:', error.request)
|
||||
// } else {
|
||||
// // 请求配置出错
|
||||
// console.error('请求配置错误:', error.message)
|
||||
// }
|
||||
// return Promise.reject(error)
|
||||
// }
|
||||
// )
|
||||
|
||||
export const userLogin = async (username, password, server, token) => {
|
||||
try {
|
||||
console.log('登录请求参数:', { username, password, server, token })
|
||||
// console.log('登录请求参数:', { username, password, server, token }); // 保留此调试日志以备将来使用,或按需移除
|
||||
const response = await axiosInstance.post('/user/login', {
|
||||
username,
|
||||
password,
|
||||
server,
|
||||
token
|
||||
})
|
||||
});
|
||||
|
||||
// 保存 token 到 localStorage
|
||||
if (response.data.access_token) {
|
||||
localStorage.setItem('access_token', response.data.access_token)
|
||||
loginSuccess(response.data.access_token, username); // 使用 username 作为 userId
|
||||
}
|
||||
|
||||
return response.data
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('登录失败:', {
|
||||
status: error.response?.status,
|
||||
data: error.response?.data,
|
||||
message: error.message,
|
||||
config: error.config
|
||||
})
|
||||
throw error
|
||||
// 错误将由响应拦截器统一处理和记录,这里可以直接抛出
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const userRegister = async (qq_code, password, server, token) => {
|
||||
try {
|
||||
@ -83,20 +78,14 @@ export const userRegister = async (qq_code, password, server, token) => {
|
||||
password,
|
||||
server,
|
||||
token
|
||||
}
|
||||
console.log('注册请求URL:', `${API_BASE_URL}/user/register/`)
|
||||
};
|
||||
// console.log('注册请求参数:', requestData); // 保留此调试日志以备将来使用,或按需移除
|
||||
|
||||
const response = await axiosInstance.post('/user/register', requestData)
|
||||
console.log('注册响应数据:', response.data)
|
||||
return response.data
|
||||
const response = await axiosInstance.post('/user/register', requestData);
|
||||
// console.log('注册响应数据:', response.data); // 保留此调试日志以备将来使用,或按需移除
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('注册请求失败:', {
|
||||
status: error.response?.status,
|
||||
data: error.response?.data,
|
||||
message: error.message,
|
||||
url: error.config?.url
|
||||
})
|
||||
throw error
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,50 +1,50 @@
|
||||
import axios from 'axios'
|
||||
import axiosInstance from './axiosConfig';
|
||||
|
||||
const API_BASE_URL = 'http://zybdatasupport.online:8000'
|
||||
// const API_BASE_URL = 'http://zybdatasupport.online:8000' // 不再需要
|
||||
|
||||
// 创建 axios 实例
|
||||
const axiosInstance = axios.create({
|
||||
baseURL: API_BASE_URL,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
},
|
||||
timeout: 10000
|
||||
})
|
||||
// // 创建 axios 实例 // 不再需要
|
||||
// const axiosInstance = axios.create({
|
||||
// baseURL: API_BASE_URL,
|
||||
// headers: {
|
||||
// 'Content-Type': 'application/json',
|
||||
// 'Accept': 'application/json',
|
||||
// 'X-Requested-With': 'XMLHttpRequest'
|
||||
// },
|
||||
// timeout: 10000
|
||||
// })
|
||||
|
||||
// 设置请求拦截器,自动添加 token
|
||||
axiosInstance.interceptors.request.use(
|
||||
config => {
|
||||
const token = localStorage.getItem('access_token')
|
||||
if (token) {
|
||||
config.headers.Authorization = `bearer ${token}`
|
||||
}
|
||||
return config
|
||||
},
|
||||
error => {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
// // 设置请求拦截器,自动添加 token // 不再需要
|
||||
// axiosInstance.interceptors.request.use(
|
||||
// config => {
|
||||
// const token = localStorage.getItem('access_token')
|
||||
// if (token) {
|
||||
// config.headers.Authorization = `Bearer ${token}`
|
||||
// }
|
||||
// return config
|
||||
// },
|
||||
// error => {
|
||||
// return Promise.reject(error)
|
||||
// }
|
||||
// )
|
||||
|
||||
// 添加响应拦截器
|
||||
axiosInstance.interceptors.response.use(
|
||||
response => response,
|
||||
error => {
|
||||
if (error.response) {
|
||||
console.error('请求错误:', {
|
||||
status: error.response.status,
|
||||
data: error.response.data,
|
||||
config: error.config
|
||||
})
|
||||
} else if (error.request) {
|
||||
console.error('网络错误:', error.request)
|
||||
} else {
|
||||
console.error('请求配置错误:', error.message)
|
||||
}
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
// // 添加响应拦截器 // 不再需要
|
||||
// axiosInstance.interceptors.response.use(
|
||||
// response => response,
|
||||
// error => {
|
||||
// if (error.response) {
|
||||
// console.error('请求错误:', {
|
||||
// status: error.response.status,
|
||||
// data: error.response.data,
|
||||
// config: error.config
|
||||
// })
|
||||
// } else if (error.request) {
|
||||
// console.error('网络错误:', error.request)
|
||||
// } else {
|
||||
// console.error('请求配置错误:', error.message)
|
||||
// }
|
||||
// return Promise.reject(error)
|
||||
// }
|
||||
// )
|
||||
|
||||
/**
|
||||
* 添加赛事
|
||||
@ -175,17 +175,18 @@ export const getSignUpResultList = async () => {
|
||||
// 更新参赛结果
|
||||
export const updateSignUpResult = async (id, data) => {
|
||||
try {
|
||||
// 更新报名信息
|
||||
console.log('更新报名信息...')
|
||||
await axiosInstance.put(`/tournament/signup/update/${id}`, {
|
||||
tournament_id: parseInt(data.tournament_id),
|
||||
type: data.team_name ? 'teamname' : 'individual',
|
||||
teamname: data.team_name || '',
|
||||
faction: data.faction || 'random',
|
||||
username: data.sign_name,
|
||||
qq: data.qq || ''
|
||||
})
|
||||
console.log('报名信息更新成功')
|
||||
// // 更新报名信息 (这部分逻辑根据您的要求被注释掉)
|
||||
// console.log('更新报名信息...')
|
||||
// await axiosInstance.put(`/tournament/signup/update/${id}`, {
|
||||
// tournament_id: parseInt(data.tournament_id),
|
||||
// type: data.team_name ? 'teamname' : 'individual',
|
||||
// teamname: data.team_name || '',
|
||||
// faction: data.faction || 'random',
|
||||
// username: data.sign_name,
|
||||
// qq: data.qq || ''
|
||||
// })
|
||||
// console.log('报名信息更新成功')
|
||||
|
||||
// 更新报名结果
|
||||
console.log('更新报名结果...')
|
||||
await axiosInstance.put(`/tournament/signup_result/update/${id}`, {
|
||||
@ -217,11 +218,11 @@ export const deleteSignUpResult = async (id) => {
|
||||
console.log('删除报名结果...')
|
||||
await axiosInstance.delete(`/tournament/signup_result/delete/${id}`)
|
||||
console.log('报名结果删除成功')
|
||||
|
||||
// 删除报名信息
|
||||
console.log('删除报名信息...')
|
||||
await axiosInstance.delete(`/tournament/signup/delete/${id}`)
|
||||
console.log('报名信息删除成功')
|
||||
|
||||
// // 删除报名信息 (这部分逻辑根据您的要求被注释掉)
|
||||
// console.log('删除报名信息...')
|
||||
// await axiosInstance.delete(`/tournament/signup/delete/${id}`)
|
||||
// console.log('报名信息删除成功')
|
||||
|
||||
return { success: true }
|
||||
} catch (error) {
|
||||
|
@ -64,7 +64,7 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, defineProps, defineEmits } from 'vue';
|
||||
import { addSignUp, updateSignUpResult } from '../../api/tournament';
|
||||
import { addSignUp, updateSignUpResult, getSignUpResultList } from '../../api/tournament';
|
||||
|
||||
const props = defineProps({
|
||||
visible: Boolean,
|
||||
@ -130,16 +130,37 @@ const submitForm = async () => {
|
||||
|
||||
try {
|
||||
if (props.isEditMode) {
|
||||
const { id, ...dataToUpdate } = form.value;
|
||||
// updateSignUpResult API 需要 player id
|
||||
await updateSignUpResult(id, dataToUpdate);
|
||||
alert('参赛记录更新成功!');
|
||||
const { id, ...originalFormData } = form.value;
|
||||
|
||||
// 为API准备数据,确保win和lose是字符串
|
||||
const dataForApi = {
|
||||
...originalFormData,
|
||||
win: String(originalFormData.win || '0'),
|
||||
lose: String(originalFormData.lose || '0')
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await getSignUpResultList();
|
||||
const existingRecord = response.find(item => item.id === id);
|
||||
if (!existingRecord) {
|
||||
throw new Error('找不到要更新的报名记录');
|
||||
}
|
||||
// 使用处理过的数据调用更新API
|
||||
await updateSignUpResult(id, dataForApi);
|
||||
alert('参赛记录更新成功!');
|
||||
} catch (error) {
|
||||
console.error('更新参赛记录失败:', error);
|
||||
errorMessage.value = error.response?.data?.detail || error.message || '更新失败,请重试';
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// addSignUp API 需要 tournament_id as id, 和 tournament_name
|
||||
// 添加新记录的逻辑
|
||||
const dataToAdd = {
|
||||
...form.value,
|
||||
id: form.value.tournament_id, // tournament_id 作为 addSignUp 中的 id
|
||||
type: form.value.team_name ? 'teamname' : 'individual'
|
||||
id: form.value.tournament_id,
|
||||
type: form.value.team_name ? 'teamname' : 'individual',
|
||||
// 注意: form.value.win 和 form.value.lose 在 initialFormState 中默认为字符串 '0'
|
||||
// 如果 addSignUp API 也严格要求字符串,且这些值可能变为数字,也需在此处 String()
|
||||
};
|
||||
await addSignUp(dataToAdd);
|
||||
alert('参赛记录添加成功!');
|
||||
|
@ -1,4 +1,6 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import { hasValidToken, getUserInfo, logoutUser } from '../utils/jwt';
|
||||
import { justLoggedIn } from '../utils/authSessionState';
|
||||
|
||||
const routes = [
|
||||
{
|
||||
@ -97,21 +99,43 @@ const router = createRouter({
|
||||
})
|
||||
|
||||
// 路由守卫
|
||||
router.beforeEach((to, from, next) => {
|
||||
const token = localStorage.getItem('access_token')
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
const requiresAuth = to.matched.some(record => record.meta.requiresAuth);
|
||||
|
||||
if (to.matched.some(record => record.meta.requiresAuth)) {
|
||||
if (!token) {
|
||||
if (requiresAuth) {
|
||||
const tokenExists = hasValidToken();
|
||||
|
||||
if (justLoggedIn.value) {
|
||||
justLoggedIn.value = false;
|
||||
if (tokenExists) {
|
||||
next();
|
||||
} else {
|
||||
logoutUser();
|
||||
next({ path: '/backend/login', query: { redirect: to.fullPath, sessionExpired: 'true' }});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (tokenExists) {
|
||||
const user = await getUserInfo();
|
||||
if (user) {
|
||||
next();
|
||||
} else {
|
||||
logoutUser();
|
||||
next({
|
||||
path: '/backend/login',
|
||||
query: { redirect: to.fullPath, sessionExpired: 'true' }
|
||||
});
|
||||
}
|
||||
} else {
|
||||
next({
|
||||
path: '/backend/login',
|
||||
query: { redirect: to.fullPath }
|
||||
})
|
||||
} else {
|
||||
next()
|
||||
});
|
||||
}
|
||||
} else {
|
||||
next()
|
||||
next();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
export default router
|
7
src/utils/authSessionState.js
Normal file
7
src/utils/authSessionState.js
Normal file
@ -0,0 +1,7 @@
|
||||
import { ref } from 'vue';
|
||||
|
||||
// This flag is used by the router guard to know if a navigation
|
||||
// is occurring immediately after a successful login.
|
||||
// If true, the guard can choose to bypass an immediate session check (e.g., getUserInfo)
|
||||
// for that first navigation, assuming the new token is valid.
|
||||
export const justLoggedIn = ref(false);
|
70
src/utils/jwt.js
Normal file
70
src/utils/jwt.js
Normal file
@ -0,0 +1,70 @@
|
||||
import axiosInstance from '../api/axiosConfig';
|
||||
import router from '../router'; // 引入 Vue Router 实例
|
||||
import { justLoggedIn } from './authSessionState'; // Import the flag
|
||||
|
||||
const USER_INFO_URL = '/user'; // 获取用户信息的API端点
|
||||
|
||||
/**
|
||||
* 检查Token是否存在且(理论上)是否在有效期内。
|
||||
* 服务端 /user接口会验证实际有效期。
|
||||
* @returns {boolean} 如果存在token则返回true,否则false。
|
||||
*/
|
||||
export const hasValidToken = () => {
|
||||
const token = localStorage.getItem('access_token');
|
||||
// 简单的存在性检查,实际有效期由 /user 接口判断
|
||||
return !!token;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取当前用户信息。
|
||||
* 如果Token无效或过期,API会返回401,响应拦截器将处理清除token。
|
||||
* @returns {Promise<Object|null>} 用户信息对象或null(如果未登录或获取失败)。
|
||||
*/
|
||||
export const getUserInfo = async () => {
|
||||
if (!hasValidToken()) {
|
||||
// console.log('jwt.js: No token found, skipping getUserInfo.');
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
// console.log('jwt.js: Attempting to fetch user info from', USER_INFO_URL);
|
||||
const response = await axiosInstance.get(USER_INFO_URL);
|
||||
// console.log('jwt.js: User info received:', response.data);
|
||||
return response.data; // 假设API成功时返回用户信息对象
|
||||
} catch (error) {
|
||||
// console.error('jwt.js: Error fetching user info:', error.response ? error.response.status : error.message);
|
||||
// 401错误会被响应拦截器处理(清除token),然后错误会传播到这里
|
||||
// 其他网络错误等也会被捕获
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理用户登出。
|
||||
* 仅清除本地存储的认证信息。
|
||||
*/
|
||||
export const logoutUser = () => { // 不再是 async,因为它不执行异步导航
|
||||
// console.log('jwt.js: logoutUser called. Clearing local storage.');
|
||||
localStorage.removeItem('access_token');
|
||||
localStorage.removeItem('user_id'); // 如果您也存储了user_id
|
||||
// 导航将由调用者(如路由守卫)处理
|
||||
};
|
||||
|
||||
/**
|
||||
* 登录成功后调用,存储token和用户信息,并处理重定向。
|
||||
* @param {string} accessToken - 从登录API获取的访问令牌。
|
||||
* @param {string} userId - 用户ID。
|
||||
*/
|
||||
export const loginSuccess = (accessToken, userId) => {
|
||||
// console.log('jwt.js: loginSuccess. Storing token and user ID.');
|
||||
localStorage.setItem('access_token', accessToken);
|
||||
if (userId) {
|
||||
localStorage.setItem('user_id', userId);
|
||||
}
|
||||
|
||||
justLoggedIn.value = true; // Set the flag before navigation
|
||||
|
||||
// 登录成功后重定向
|
||||
// 尝试获取之前尝试访问的路径,否则重定向到默认路径(例如仪表盘)
|
||||
const redirectPath = router.currentRoute.value.query.redirect || '/backend/dashboard';
|
||||
router.replace(redirectPath);
|
||||
};
|
@ -38,13 +38,21 @@
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="dashboard-content non-admin-view">
|
||||
<div class="button-group">
|
||||
<button @click="handleLogout" class="logout-button">
|
||||
退出登录
|
||||
</button>
|
||||
<button @click="goToHomePage" class="home-button">
|
||||
返回主页面
|
||||
</button>
|
||||
<div class="non-admin-card">
|
||||
<div class="welcome-message">欢迎回来!</div>
|
||||
<div class="info-text">
|
||||
您当前没有管理员权限。请选择以下操作:
|
||||
</div>
|
||||
<div class="button-group">
|
||||
<button @click="goToHomePage" class="home-button">
|
||||
<!-- Optional: Add an icon here -->
|
||||
返回主页面
|
||||
</button>
|
||||
<button @click="handleLogout" class="logout-button">
|
||||
<!-- Optional: Add an icon here -->
|
||||
退出登录
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -236,37 +244,71 @@ html, body, .dashboard-wrapper, .admin-layout {
|
||||
transition: margin-left 0.3s ease;
|
||||
}
|
||||
|
||||
/* Non-Admin View (Centered content, similar to before but within new wrapper) */
|
||||
/* Non-Admin View Enhancements */
|
||||
.dashboard-content.non-admin-view {
|
||||
display: flex; /* Use flex to center content */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%; /* Take full height of the wrapper */
|
||||
min-height: 100vh; /* Use min-height to ensure it covers viewport even if content is small */
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
/* background-color: #fff; Already white from admin-main-content, but can be explicit if needed */
|
||||
padding: 30px;
|
||||
background-color: #f0f2f5; /* Consistent light background */
|
||||
text-align: center; /* Center text for any messages */
|
||||
}
|
||||
.non-admin-view .button-group {
|
||||
background-color: #fff;
|
||||
padding: 30px 40px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
text-align: center;
|
||||
max-width: 400px;
|
||||
|
||||
.non-admin-view .non-admin-card {
|
||||
background-color: #ffffff;
|
||||
padding: 40px 50px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
|
||||
max-width: 450px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center; /* Center buttons if they are directly in this card */
|
||||
}
|
||||
|
||||
.non-admin-view .welcome-message {
|
||||
font-size: 1.5rem; /* Slightly larger welcome/info text */
|
||||
color: #333;
|
||||
margin-bottom: 15px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.non-admin-view .info-text {
|
||||
font-size: 0.95rem;
|
||||
color: #666;
|
||||
margin-bottom: 30px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.non-admin-view .button-group {
|
||||
/* Removed background, padding, shadow from here as it's now on non-admin-card */
|
||||
width: 100%; /* Button group takes full width of the card */
|
||||
display: flex;
|
||||
flex-direction: column; /* Stack buttons vertically */
|
||||
gap: 15px; /* Space between buttons */
|
||||
}
|
||||
|
||||
.non-admin-view .button-group button {
|
||||
padding: 10px 20px;
|
||||
border-radius: 6px;
|
||||
padding: 12px 25px; /* Slightly larger padding */
|
||||
border-radius: 8px; /* Slightly more rounded */
|
||||
font-weight: 500;
|
||||
transition: background-color 0.2s;
|
||||
font-size: 1rem; /* Standardized font size */
|
||||
transition: background-color 0.2s, transform 0.1s;
|
||||
border: none;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
margin: 8px; /* Provides spacing around buttons, e.g., 8px top/bottom, 8px left/right */
|
||||
min-width: 120px; /* Ensure buttons have a decent minimum width */
|
||||
width: 100%; /* Make buttons take full width of their container */
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.non-admin-view .button-group button:hover {
|
||||
transform: translateY(-2px); /* Subtle lift on hover */
|
||||
}
|
||||
|
||||
.non-admin-view .button-group .home-button {
|
||||
|
Loading…
x
Reference in New Issue
Block a user