feat: 1. 完善全局路由的访问权限的验证。 2. 完善接口返回的树形路由结构 3.优化评查点列表的查询,改用表连接的方式,废弃使用数据库的rpc函数,同时进行地区隔离和权限隔离。

4. 删除冗余的评查文件列表。      5.完善上传文档 页面初始化查询数据的时候 查询文件类型(改成动态指定)  6. 添加获取入口模块的查询接口。    7.完善服务端中判断token的有效性,失效则跳转到登录页。
8. 重构layout和sidebar的页面,改成由动态权限路由来渲染对应的菜单栏。       9.重构入口页面,通过动态查询根据不同地区的人返回不同的入口。
This commit is contained in:
2025-11-20 01:35:30 +08:00
parent adfb84a31d
commit 2edde8a8ab
23 changed files with 1201 additions and 2154 deletions
+43 -6
View File
@@ -24,21 +24,41 @@ export async function loader({ request }: LoaderFunctionArgs) {
// ⚠️ 不再检查服务端 session 认证
// 认证检查改为在客户端通过 localStorage 进行
// 获取重定向URL
// 获取重定向URL和错误参数
const url = new URL(request.url);
const redirectTo = url.searchParams.get("redirect") || "/";
const urlError = url.searchParams.get("error");
const session = await getSession(request);
// 读取 flash 消息(来自 callback 的错误)
const loginError = session.get("loginError");
// 将URL错误参数转换为友好的错误消息
let urlErrorMessage: string | null = null;
if (urlError) {
switch (urlError) {
case 'no_role':
urlErrorMessage = '用户角色信息缺失,请重新登录';
break;
case 'no_token':
urlErrorMessage = '认证令牌缺失,请重新登录';
break;
case 'session_expired':
urlErrorMessage = '会话已过期,请重新登录';
break;
default:
urlErrorMessage = '登录状态异常,请重新登录';
}
}
// 提交 session 以清除 flash 消息
if (loginError) {
const { sessionStorage } = await import("~/api/login/auth.server");
return Response.json({
redirectTo,
flashError: loginError
flashError: loginError,
urlError: urlErrorMessage
}, {
headers: {
"Set-Cookie": await sessionStorage.commitSession(session)
@@ -48,7 +68,8 @@ export async function loader({ request }: LoaderFunctionArgs) {
return Response.json({
redirectTo,
flashError: null
flashError: null,
urlError: urlErrorMessage
});
}
@@ -107,6 +128,11 @@ export async function action({ request }: ActionFunctionArgs) {
}, { status: 500 });
}
// 测试,管理员账密返回的时候默认给没有area信息
// if(!user_info.area){
// user_info.area = '梅州'
// }
// 🔑 将后端返回的 issued_time 转换为时间戳(毫秒)
let tokenIssuedAt = Date.now(); // 默认使用当前时间
if (issued_time) {
@@ -141,6 +167,7 @@ export async function action({ request }: ActionFunctionArgs) {
ou_name: user_info.ou_name,
is_leader: user_info.is_leader,
user_role: user_info.user_role,
area: user_info.area, // 🔑 用户所属地区
sub: user_info.sub
})));
callbackUrl.searchParams.set('redirectTo', redirectTo);
@@ -163,6 +190,7 @@ export async function action({ request }: ActionFunctionArgs) {
ou_name: user_info.ou_name,
is_leader: user_info.is_leader,
user_role: user_info.user_role,
area: user_info.area, // 🔑 用户所属地区
sub: user_info.sub
}
});
@@ -185,11 +213,12 @@ export default function Login() {
const [password, setPassword] = useState("");
const [passwordLoginError, setPasswordLoginError] = useState<string | null>(null);
// 从 loaderData 中获取 OAuth 回调的错误信息
// 从 loaderData 中获取错误信息
const oauthError = loaderData?.flashError;
const urlError = loaderData?.urlError;
// 显示的错误信息:密码登录错误优先,其次是 OAuth 错误
const error = passwordLoginError || oauthError;
// 显示的错误信息:密码登录错误优先,其次是 URL 错误,最后是 OAuth 错误
const error = passwordLoginError || urlError || oauthError;
const isLocked = false; // 可以从后端响应中获取
const retryCount = 0;
const remainingAttempts = 5;
@@ -283,6 +312,14 @@ export default function Login() {
}
}, [fetcher.data]);
// 显示URL错误参数的Toast提示
useEffect(() => {
if (urlError) {
console.warn("⚠️ [Login] 检测到URL错误参数:", urlError);
toastService.error(urlError);
}
}, [urlError]);
useEffect(() => {
// 🔑 只在 token 过期时清理客户端存储
// 检查 URL 参数中是否有 expired=true 标识