Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions frontend/.env.development
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ VITE_APP_ENV=development
VITE_APP_TITLE=fastapi_vue3_admin

# 网络请求公用地址
VITE_API_BASE_URL=http://localhost:8001
# VITE_API_BASE_URL=http://service.fastapiadmin.com
# VITE_API_BASE_URL=http://localhost:8001
VITE_API_BASE_URL=http://service.fastapiadmin.com

# 代理前缀
VITE_APP_BASE_API=/api/v1
Expand Down
4 changes: 1 addition & 3 deletions frontend/src/components/Notification/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,7 @@ onMounted(() => {
featchMyNotice();
});

onBeforeUnmount(() => {
featchMyNotice();
});

</script>

<style lang="scss" scoped></style>
4 changes: 0 additions & 4 deletions frontend/src/enums/api/result.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,4 @@ export const enum ResultEnum {
*/
ACCESS_TOKEN_INVALID = 401,

/**
* 拒绝访问
*/
ACCESS_DENIED = 403,
}
2 changes: 0 additions & 2 deletions frontend/src/plugins/permission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ export function setupPermission() {
// 处理已登录用户的路由访问
await handleAuthenticatedUser(to, from, next);
} else {
console.log("❌ User not logged in");

// 未登录用户的处理
if (whiteList.includes(to.path)) {
next();
Expand Down
5 changes: 1 addition & 4 deletions frontend/src/store/modules/permission.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,8 @@ export const usePermissionStore = defineStore("permission", () => {

routesLoaded.value = true;

console.log("✅ 动态路由:", (dynamicRoutes));
return dynamicRoutes;
} catch (error) {
console.error("❌ 生成路由失败:", error);

} catch (error: any) {
// 即使失败也要设置状态,避免无限重试
routesLoaded.value = false;

Expand Down
33 changes: 7 additions & 26 deletions frontend/src/store/modules/user.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,34 +46,16 @@ export const useUserStore = defineStore("user", {
},

// 登录
login(LoginFormData: LoginFormData) {
return new Promise<void>((resolve, reject) => {
AuthAPI.login(LoginFormData)
.then((response) => {
// 保存记住我状态和token
this.rememberMe = LoginFormData.remember;
Auth.setTokens(response.data.data.access_token, response.data.data.refresh_token, this.rememberMe);
resolve();
})
.catch((error) => {
reject(error);
});
});
async login(LoginFormData: LoginFormData) {
const response = await AuthAPI.login(LoginFormData)
this.rememberMe = LoginFormData.remember;
Auth.setTokens(response.data.data.access_token, response.data.data.refresh_token, this.rememberMe);
},

// 登出
logout() {
return new Promise<void>((resolve, reject) => {
AuthAPI.logout({token: Auth.getAccessToken()})
.then(() => {
// 重置所有系统状态
this.resetAllState();
resolve();
})
.catch((error) => {
reject(error);
});
});
async logout() {
await AuthAPI.logout({token: Auth.getAccessToken()});
this.resetAllState();
},

// 重置所有状态
Expand Down Expand Up @@ -106,7 +88,6 @@ export const useUserStore = defineStore("user", {
resolve();
})
.catch((error) => {
console.log(" refreshToken 刷新失败", error);
reject(error);
});
});
Expand Down
65 changes: 22 additions & 43 deletions frontend/src/utils/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ const httpRequest: AxiosInstance = axios.create({
/**
* 请求拦截器 - 添加 Authorization 头
*/
httpRequest.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
httpRequest.interceptors.request.use((config: InternalAxiosRequestConfig) => {
const accessToken = Auth.getAccessToken();

// 如果 Authorization 设置为 no-auth,则不携带 Token
Expand All @@ -32,7 +31,6 @@ httpRequest.interceptors.request.use(
return config;
},
(error) => {
console.error("请求异常:", error);
ElMessage.error(error);
return Promise.reject(error);
}
Expand All @@ -41,57 +39,38 @@ httpRequest.interceptors.request.use(
/**
* 响应拦截器 - 统一处理响应和错误
*/
httpRequest.interceptors.response.use(
(response: AxiosResponse<ApiResponse>) => {
httpRequest.interceptors.response.use((response: AxiosResponse<ApiResponse>) => {
// 如果响应是二进制流,则直接返回(用于文件下载、Excel 导出等)
if (response?.config?.responseType === "blob") {
if (response.config.responseType === "blob") {
return response;
}

const data = response.data;

// 请求成功
if (data.code === ResultEnum.SUCCESS) {
// 只有不是 get 请求时才显示成功消息
if (response?.config?.method?.toLowerCase() !== 'get') {
ElMessage.success(data.msg);
}
return response;
} else if (data.code === ResultEnum.ERROR) {
if (data.code != ResultEnum.SUCCESS) {
ElMessage.error(data.msg);
return Promise.reject(response);
}
return response;
}, async (error: AxiosError<ApiResponse>) => {
const data = error.response?.data;

if (data?.status_code === ResultEnum.ACCESS_TOKEN_INVALID) {
await redirectToLogin("登录已过期,请重新登录");
return Promise.reject(new Error(data.msg));
} else if (data?.code === ResultEnum.ERROR) {
ElMessage.error(data.msg || "请求错误");
return Promise.reject(new Error(data.msg || "请求错误"));
} else if (data.code === ResultEnum.EXCEPTION) {
} else if (data?.code === ResultEnum.EXCEPTION) {
ElMessage.error(data.msg || "服务异常");
return Promise.reject(new Error(data.msg || "服务异常"));
} else {
ElMessage.error(data.msg || "请求异常");
return Promise.reject(new Error(data.msg || "请求异常"));
}
},
async (error: AxiosError) => {
console.error("响应异常 " + error);

const { response } = error;

// 网络错误或服务器无响应
if (!response) {
ElMessage.error("网络连接失败,请检查网络设置");
return Promise.reject(error);
}

const { status_code, msg } = response.data as ApiResponse;

switch (status_code) {
case ResultEnum.ACCESS_TOKEN_INVALID:
// Access Token 过期
await redirectToLogin("登录已过期,请重新登录");
return Promise.reject(new Error(msg || "Refresh Token Invalid"));

default:
return Promise.reject(new Error(msg || "响应错误"));
} else {
ElMessage.error(error.message || "请求异常");
return Promise.reject(new Error(error.message || "请求异常"));
}
}
);
);

/**
* 重定向到登录页面
Expand All @@ -110,8 +89,8 @@ async function redirectToLogin(message: string = "请重新登录"): Promise<voi
// 跳转到登录页,保留当前路由用于登录后跳转
const currentPath = router.currentRoute.value.fullPath;
await router.push(`/login?redirect=${encodeURIComponent(currentPath)}`);
} catch (error) {
console.error("Redirect to login error:", error);
} catch (error: any) {
ElMessage.error(error.message);
}
}

Expand Down
72 changes: 10 additions & 62 deletions frontend/src/views/dashboard/analysis.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,89 +28,37 @@
仓库
</div>
<div class="mt-3 whitespace-nowrap">
<el-link href="https://gitee.com/youlaiorg/vue3-element-admin">
<el-link href="https://gitee.com/tao__tao/fastapi_vue3_admin">
<div class="i-svg:gitee text-lg color-#F76560" />
</el-link>
<el-divider direction="vertical" />
<el-link href="https://github.com/youlaitech/vue3-element-admin">
<el-link href="https://github.com/1014TaoTao/fastapi_vue3_admin">
<div class="i-svg:github text-lg color-#4080FF" />
</el-link>
<el-divider direction="vertical" />
<el-link href="https://gitcode.com/youlai/vue3-element-admin">
<el-link href="https://gitcode.com/qq_36002987/fastapi_vue3_admin">
<div class="i-svg:gitcode text-lg color-#FF9A2E" />
</el-link>
</div>
</div>

<!-- 文档 -->
<div>
<div class="font-bold color-#4080ff text-sm flex items-center">
<el-icon class="mr-2px">
<Document />
</el-icon>
文档
</div>
<div class="mt-3 whitespace-nowrap">
<el-link href="https://juejin.cn/post/7228990409909108793">
<div class="i-svg:juejin text-lg" />
</el-link>
<el-divider direction="vertical" />
<el-link href="https://youlai.blog.csdn.net/article/details/130191394">
<div class="i-svg:csdn text-lg" />
</el-link>
<el-divider direction="vertical" />
<el-link href="https://www.cnblogs.com/haoxianrui/p/17331952.html">
<div class="i-svg:cnblogs text-lg" />
</el-link>
</div>
</div>

<!-- 视频 -->
<div>
<div class="font-bold color-#f76560 text-sm flex items-center">
<el-icon class="mr-2px">
<VideoCamera />
</el-icon>
视频
</div>
<div class="mt-3 whitespace-nowrap">
<el-link href="https://www.bilibili.com/video/BV1eFUuYyEFj">
<div class="i-svg:bilibili text-lg" />
</el-link>
</div>
</div>
</div>
</div>

<!-- 移动端图标区域 -->
<div class="w-full sm:hidden mt-3">
<div class="flex justify-end space-x-4 overflow-x-auto">
<!-- 仓库图标 -->
<el-link href="https://gitee.com/youlaiorg/vue3-element-admin">
<el-link href="https://gitee.com/tao__tao/fastapi_vue3_admin">
<div class="i-svg:gitee text-lg color-#F76560" />
</el-link>
<el-link href="https://github.com/youlaitech/vue3-element-admin">
<el-link href="https://github.com/1014TaoTao/fastapi_vue3_admin">
<div class="i-svg:github text-lg color-#4080FF" />
</el-link>
<el-link href="https://gitcode.com/youlai/vue3-element-admin">
<el-link href="https://gitcode.com/qq_36002987/fastapi_vue3_admin">
<div class="i-svg:gitcode text-lg color-#FF9A2E" />
</el-link>

<!-- 文档图标 -->
<el-link href="https://juejin.cn/post/7228990409909108793">
<div class="i-svg:juejin text-lg" />
</el-link>
<el-link href="https://youlai.blog.csdn.net/article/details/130191394">
<div class="i-svg:csdn text-lg" />
</el-link>
<el-link href="https://www.cnblogs.com/haoxianrui/p/17331952.html">
<div class="i-svg:cnblogs text-lg" />
</el-link>

<!-- 视频图标 -->
<el-link href="https://www.bilibili.com/video/BV1eFUuYyEFj">
<div class="i-svg:bilibili text-lg" />
</el-link>
</div>
</div>
</div>
Expand Down Expand Up @@ -287,7 +235,7 @@
<template #header>
<div class="flex-x-between">
<span class="header-title">最新动态</span>
<el-link type="primary" underline="never" href="https://gitee.com/youlaiorg/vue3-element-admin/releases">
<el-link type="primary" underline="never" href="https://gitee.com/tao__tao/fastapi_vue3_admin/releases">
完整记录
<el-icon class="link-icon">
<TopRight />
Expand Down Expand Up @@ -362,23 +310,23 @@ const vesionList = ref<VersionItem[]>([
title: "v3.2.1",
date: dayjs().format('YYYY-MM-DD HH:mm:ss'),
content: "优化性能,修复若干小bug。",
link: "https://gitee.com/youlaiorg/vue3-element-admin/releases",
link: "https://gitee.com/tao__tao/fastapi_vue3_admin/releases",
tag: "更新",
},
{
id: "2",
title: "v3.2.0",
date: dayjs().subtract(1, 'day').format('YYYY-MM-DD HH:mm:ss'),
content: "新增用户行为分析功能。",
link: "https://gitee.com/youlaiorg/vue3-element-admin/releases",
link: "https://gitee.com/tao__tao/fastapi_vue3_admin/releases",
tag: "新功能",
},
{
id: "3",
title: "v3.1.0",
date: dayjs().subtract(3, 'day').format('YYYY-MM-DD HH:mm:ss'),
content: "优化权限管理系统。",
link: "https://gitee.com/youlaiorg/vue3-element-admin/releases",
link: "https://gitee.com/tao__tao/fastapi_vue3_admin/releases",
tag: "优化",
},
]);
Expand Down
26 changes: 6 additions & 20 deletions frontend/src/views/system/auth/components/Login.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,26 +39,16 @@
<!-- 验证码 -->
<el-form-item v-if="captchaState.enable" prop="captcha">
<div flex>
<el-input
v-model.trim="loginForm.captcha"
:placeholder="t('login.captchaCode')"
clearable
@keyup.enter="handleLoginSubmit"
>
<el-input v-model.trim="loginForm.captcha" :placeholder="t('login.captchaCode')" clearable @keyup.enter="handleLoginSubmit" >
<template #prefix>
<div class="i-svg:captcha" />
</template>
</el-input>
<div cursor-pointer flex-center ml-10px @click="getCaptcha">
<el-icon v-if="codeLoading" class="is-loading"><Loading /></el-icon>

<el-image
v-else
object-cover

:src="captchaState.img_base"

/>
<div cursor-pointer flex-center ml-10px >
<el-icon v-if="codeLoading" class="is-loading">
<Loading />
</el-icon>
<el-image v-else object-cover :src="captchaState.img_base" @click="getCaptcha" />
</div>
</div>
</el-form-item>
Expand Down Expand Up @@ -184,8 +174,6 @@ async function getCaptcha() {
const response = await AuthAPI.getCaptcha()
loginForm.captcha_key = response.data.data.key;
captchaState.img_base = response.data.data.img_base;
} catch (error: any) {
console.error(error.message);
} finally {
codeLoading.value = false;
}
Expand All @@ -208,7 +196,6 @@ async function handleLoginSubmit() {
// 3. 登录成功,让路由守卫处理跳转逻辑
// 解析目标地址,但不直接跳转
const redirect = resolveRedirectTarget(route.query);
console.log("🎉 Login successful, target redirect:", redirect);

// 通过替换当前路由触发路由守卫,让守卫处理后续的路由生成和跳转
await router.replace(redirect);
Expand All @@ -219,7 +206,6 @@ async function handleLoginSubmit() {
} catch (error) {
// 5. 统一错误处理
getCaptcha(); // 刷新验证码
console.error(error);
} finally {
loading.value = false;
}
Expand Down
Loading