feat: 1. 完善全局路由的访问权限的验证。 2. 完善接口返回的树形路由结构 3.优化评查点列表的查询,改用表连接的方式,废弃使用数据库的rpc函数,同时进行地区隔离和权限隔离。
4. 删除冗余的评查文件列表。 5.完善上传文档 页面初始化查询数据的时候 查询文件类型(改成动态指定) 6. 添加获取入口模块的查询接口。 7.完善服务端中判断token的有效性,失效则跳转到登录页。 8. 重构layout和sidebar的页面,改成由动态权限路由来渲染对应的菜单栏。 9.重构入口页面,通过动态查询根据不同地区的人返回不同的入口。
This commit is contained in:
+102
-51
@@ -4,6 +4,7 @@ import { type MetaFunction, type ActionFunctionArgs, LoaderFunctionArgs, redirec
|
||||
import styles from "~/styles/pages/home.css?url";
|
||||
import dayjs from 'dayjs';
|
||||
import { getUserSession, logout } from "~/api/login/auth.server";
|
||||
import { toastService } from '~/components/ui';
|
||||
|
||||
export const links = () => [
|
||||
{ rel: "stylesheet", href: styles }
|
||||
@@ -28,15 +29,28 @@ export async function action({ request }: ActionFunctionArgs) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 获取用户信息(不再检查服务端认证)
|
||||
// 获取用户信息
|
||||
export async function loader({ request }: LoaderFunctionArgs) {
|
||||
// ⚠️ 不再检查服务端 session 认证
|
||||
// 认证检查由 ClientAuthGuard 在客户端进行
|
||||
// 🔒 认证检查已在 getUserSession() 中统一处理
|
||||
// 如果未认证,会自动重定向到登录页,不会执行到这里
|
||||
const { userRole, userInfo, frontendJWT } = await getUserSession(request);
|
||||
|
||||
const { userRole, userInfo } = await getUserSession(request);
|
||||
// 🔑 获取用户地区并查询入口模块
|
||||
const userArea = userInfo?.area || null;
|
||||
// console.log('🔍 [Index Loader] 用户地区:', userArea);
|
||||
// console.log('🔍 [Index Loader] 用户角色:', userRole);
|
||||
|
||||
// 返回用户信息给客户端(可能为空)
|
||||
return Response.json({ userRole, userInfo });
|
||||
let entryModules = [];
|
||||
if (userRole && frontendJWT) {
|
||||
const { getEntryModules } = await import('~/api/home/home');
|
||||
entryModules = await getEntryModules(userRole,userArea, frontendJWT);
|
||||
console.log(`📦 [Index Loader] 获取到 ${entryModules.length} 个入口模块`);
|
||||
} else {
|
||||
console.warn('⚠️ [Index Loader] 用户角色为空,返回空模块列表');
|
||||
}
|
||||
|
||||
// 返回用户信息和入口模块给客户端
|
||||
return Response.json({ userRole, userInfo, entryModules });
|
||||
}
|
||||
|
||||
export default function Index() {
|
||||
@@ -102,21 +116,73 @@ export default function Index() {
|
||||
}, []);
|
||||
|
||||
// 处理模块点击
|
||||
const handleModuleClick = (path: string, reviewType: string) => {
|
||||
// 将reviewType存入sessionStorage
|
||||
if (typeof window !== 'undefined') {
|
||||
sessionStorage.setItem('reviewType', reviewType);
|
||||
const handleModuleClick = (module: typeof loaderData.entryModules[0]) => {
|
||||
// 提取文档类型 IDs
|
||||
const typeIds = module.document_types?.map(dt => dt.id) || [];
|
||||
|
||||
// 🔑 验证文档类型(智慧法务大模型除外)
|
||||
if (module.name !== '智慧法务大模型' && typeIds.length === 0) {
|
||||
toastService.error('该入口尚未关联文档类型,无法进入');
|
||||
console.warn('⚠️ [Index] 模块未关联文档类型:', module.name);
|
||||
return; // 阻止进入
|
||||
}
|
||||
navigate(path);
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
// 🔑 存储到 sessionStorage(用于客户端请求)
|
||||
if (typeIds.length > 0) {
|
||||
sessionStorage.setItem('documentTypeIds', JSON.stringify(typeIds));
|
||||
|
||||
// console.log('📝 [Index] 存储到客户端 sessionStorage:', typeIds);
|
||||
} else {
|
||||
// 清空文档类型数据
|
||||
sessionStorage.removeItem('documentTypeIds');
|
||||
}
|
||||
|
||||
// 存储模块信息
|
||||
sessionStorage.setItem('selectedModuleId', String(module.id));
|
||||
sessionStorage.setItem('selectedModuleName', module.name);
|
||||
sessionStorage.setItem('selectedModulePicPath', module.path)
|
||||
}
|
||||
|
||||
// 🔑 根据模块名称决定跳转路径
|
||||
let targetPath = '/home'; // 默认跳转到首页
|
||||
|
||||
if (module.name.includes('合同')) {
|
||||
// 合同相关模块 → 跳转到合同模板搜索
|
||||
targetPath = '/contract-template/search';
|
||||
// console.log('📌 [Index] 合同模块,跳转到:', targetPath);
|
||||
} else if (module.name === '智慧法务大模型') {
|
||||
// 智慧法务大模型 → 跳转到 AI 对话
|
||||
targetPath = '/chat-with-llm';
|
||||
// console.log('📌 [Index] 智慧法务大模型,跳转到:', targetPath);
|
||||
} else {
|
||||
// console.log('📌 [Index] 其他模块,跳转到:', targetPath);
|
||||
}
|
||||
|
||||
navigate(targetPath);
|
||||
};
|
||||
|
||||
// 处理键盘事件
|
||||
const handleKeyDown = (path: string, reviewType: string, e: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
const handleKeyDown = (module: typeof loaderData.entryModules[0], e: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
handleModuleClick(path, reviewType);
|
||||
handleModuleClick(module);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取模块图标(根据模块 path 或 id)
|
||||
const getModuleIcon = (module: typeof loaderData.entryModules[0]) => {
|
||||
// 根据 path 判断图标
|
||||
if (module.path?.includes('ht')) {
|
||||
return '/images/icon_hetong.png';
|
||||
} else if (module.path?.includes('aj')) {
|
||||
return '/images/icon_anjuan.png';
|
||||
} else if (module.path?.includes('nw')) {
|
||||
return '/images/icon_assistant.png';
|
||||
}
|
||||
// 默认图标
|
||||
return '/images/icon_assistant.png';
|
||||
};
|
||||
|
||||
// 处理登出
|
||||
const handleLogout = () => {
|
||||
// 清除sessionStorage中的所有数据
|
||||
@@ -182,46 +248,31 @@ export default function Index() {
|
||||
<h1 className="welcome-text">- 欢迎来到智慧法务平台 -</h1>
|
||||
|
||||
<div className="modules-container">
|
||||
{/* 合同管理模块 - 51708端口时隐藏 */}
|
||||
{!isPort51707 && (
|
||||
<div
|
||||
className="module-card"
|
||||
onClick={() => handleModuleClick('/contract-template/search', 'contract')}
|
||||
onKeyDown={(e) => handleKeyDown('/contract-template/search', 'contract', e)}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label="合同管理"
|
||||
>
|
||||
<img src="/images/icon_hetong.png" alt="合同管理" className="w-12 h-12 mx-1" />
|
||||
<span className="module-name">合同管理</span>
|
||||
{/* 动态渲染入口模块 */}
|
||||
{loaderData.entryModules && loaderData.entryModules.length > 0 ? (
|
||||
loaderData.entryModules.map((module) => (
|
||||
<div
|
||||
key={module.id}
|
||||
className="module-card"
|
||||
onClick={() => handleModuleClick(module)}
|
||||
onKeyDown={(e) => handleKeyDown(module, e)}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label={module.name}
|
||||
>
|
||||
<img
|
||||
src={getModuleIcon(module)}
|
||||
alt={module.name}
|
||||
className="w-12 h-12 mx-1"
|
||||
/>
|
||||
<span className="module-name">{module.name}</span>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className="text-center text-gray-500 py-8">
|
||||
暂无可用模块
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 案卷智能评查模块 */}
|
||||
<div
|
||||
className="module-card"
|
||||
onClick={() => handleModuleClick('/home', 'record')}
|
||||
onKeyDown={(e) => handleKeyDown('/home', 'record', e)}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label="案卷智能评查"
|
||||
>
|
||||
<img src="/images/icon_anjuan.png" alt="案卷智能评查" className="w-12 h-12" />
|
||||
<span className="module-name">案卷智能评查</span>
|
||||
</div>
|
||||
|
||||
{/* 智慧法务大模型模块 */}
|
||||
<div
|
||||
className="module-card"
|
||||
onClick={() => handleModuleClick('/chat-with-llm', 'model')}
|
||||
onKeyDown={(e) => handleKeyDown('/chat-with-llm', 'model', e)}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label="智慧法务大模型"
|
||||
>
|
||||
<img src="/images/icon_assistant.png" alt="智慧法务大模型" className="w-12 h-12" />
|
||||
<span className="module-name">智慧法务大模型</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
Reference in New Issue
Block a user