From 360b5a846d01ec4a473fe6a00576023402b6983b Mon Sep 17 00:00:00 2001 From: Wren Date: Mon, 11 Aug 2025 10:40:56 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BD=BF=E7=94=A8=E4=BD=93?= =?UTF-8?q?=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/cross-checking/cross-files.ts | 2 + .../cross-checking/DocumentListModal.tsx | 71 +++++-------------- app/config/api-config.ts | 6 +- app/routes/callback.tsx | 67 +++++++++++------ app/routes/cross-checking._index.tsx | 2 +- app/routes/documents._index.tsx | 52 +++++++++++--- app/routes/rules-files.tsx | 32 +++++++++ app/routes/rules._index.tsx | 29 +++++++- 8 files changed, 172 insertions(+), 89 deletions(-) diff --git a/app/api/cross-checking/cross-files.ts b/app/api/cross-checking/cross-files.ts index 435e248..8218c50 100644 --- a/app/api/cross-checking/cross-files.ts +++ b/app/api/cross-checking/cross-files.ts @@ -84,6 +84,8 @@ export interface TaskDocument { message: string; }>; final_score: number; + score_summary: string ; + score_percent?: number | null; pass_count: number; warning_count: number; fail_count: number; diff --git a/app/components/cross-checking/DocumentListModal.tsx b/app/components/cross-checking/DocumentListModal.tsx index bc16f32..12bbe05 100644 --- a/app/components/cross-checking/DocumentListModal.tsx +++ b/app/components/cross-checking/DocumentListModal.tsx @@ -1,4 +1,3 @@ - import { Modal } from '../ui/Modal'; import { Table } from '../ui/Table'; import { Button } from '../ui/Button'; @@ -91,47 +90,6 @@ export function DocumentListModal({ }; - // 渲染问题摘要 - const renderIssues = (file: TaskDocument) => { - // 如果文件有问题信息 - if (file.issues && file.issues.length > 0) { - // 最多显示2个问题 - const displayIssues = file.issues.slice(0, 2); - - return ( -
- {displayIssues.map((issue, index) => ( -
- - {issue.message} -
- ))} - - {file.issues.length > 2 && ( -
- 还有 {file.issues.length - 2} 个问题... -
- )} -
- ); - } - - // 如果没有问题信息,根据状态显示 - if (file.evaluations_status === 1) { - return ( -
- 所有评查点均通过 -
- ); - } - - // 其他状态显示占位符 - return
-
; - }; // 获取文件大小的友好显示 const formatFileSize = (bytes: number) => { @@ -252,12 +210,8 @@ export function DocumentListModal({ render: (_: unknown, file: TaskDocument) => (
{file.final_score ? ( - = 90 ? 'text-green-600' : - file.final_score >= 70 ? 'text-yellow-600' : - 'text-red-600' - }`}> - {file.final_score} + + {file.score_summary} ) : ( - @@ -265,18 +219,27 @@ export function DocumentListModal({
) }, + { + title: "评查分数百分化", + key: "scorePercent", + width: "10%", + render: (_: unknown, file: TaskDocument) => { + const value: number | null | undefined = file.score_percent as number | null | undefined; + if (value === null || value === undefined || Number.isNaN(value)) { + return -; + } + const numericValue = typeof value === 'string' ? Number(value) : value; + const normalized = numericValue <= 1 ? numericValue * 100 : numericValue; + const display = `${Number(normalized.toFixed(1))}%`; + return {display}; + } + }, { title: '审核状态', key: 'auditStatus', width: '8%', render: (_: unknown, file: TaskDocument) => renderAuditStatus(file) }, - { - title: "问题摘要", - key: "issues", - width: "20%", - render: (_: unknown, file: TaskDocument) => renderIssues(file) - }, { title: "操作", key: "operation", diff --git a/app/config/api-config.ts b/app/config/api-config.ts index c800ac6..af96e69 100644 --- a/app/config/api-config.ts +++ b/app/config/api-config.ts @@ -73,9 +73,9 @@ const portConfigs: Record> = { // 主要 // 梅州 '51703': { - baseUrl: 'http://10.79.97.17:8000', - documentUrl: 'http://10.79.97.17:8000/docauditai/', - uploadUrl: 'http://10.79.97.17:8000/admin/documents' + baseUrl: 'http://172.16.0.55:8000', + documentUrl: 'http://172.16.0.55:8000/docauditai/', + uploadUrl: 'http://172.16.0.55:8000/admin/documents' }, diff --git a/app/routes/callback.tsx b/app/routes/callback.tsx index 90066e5..f1dfee7 100644 --- a/app/routes/callback.tsx +++ b/app/routes/callback.tsx @@ -1,11 +1,10 @@ import { type LoaderFunctionArgs, redirect } from "@remix-run/node"; -import { OAuthClient } from "~/api/login/oauth-client"; -import { OAUTH_CONFIG } from "~/config/api-config"; import { createUserSession, saveUserInfo } from "~/api/login/auth.server"; import { JWTUtils, type UserInfoForJWT } from "~/utils/jwt"; export async function loader({ request }: LoaderFunctionArgs) { const url = new URL(request.url); + const origin = url.origin; // 获取请求的源 (e.g., "http://10.79.97.17:51703") const code = url.searchParams.get("code"); const state = url.searchParams.get("state"); const error = url.searchParams.get("error"); @@ -42,28 +41,56 @@ export async function loader({ request }: LoaderFunctionArgs) { try { console.log("🔧 开始处理OAuth2.0回调"); - // 创建OAuth客户端 - const oauthClient = new OAuthClient(OAUTH_CONFIG); - console.log("✅ OAuth客户端创建成功"); + // --- 修改开始: 不再直接调用OAuthClient,而是通过内部代理API --- - // 获取访问令牌 - console.log("🔧 开始获取访问令牌..."); - const tokenResponse = await oauthClient.getAccessToken(code); - if (!tokenResponse) { - console.error("❌ 获取访问令牌失败"); - return redirect("/login?error=token_error"); - } - console.log("✅ 访问令牌获取成功"); + // 获取访问令牌 (通过代理) + console.log(`🔧 [Callback] 开始通过内部代理获取访问令牌... (目标: ${origin}/api/oauth/token)`); + const proxyResponse = await fetch(`${origin}/api/oauth/token`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ code }), + }); - // 获取用户信息 - const userInfo = await oauthClient.getUserInfo(tokenResponse.access_token); - if (!userInfo || !userInfo.success) { - console.error("获取用户信息失败:", userInfo); - return redirect("/login?error=userinfo_error"); + const tokenResponse = await proxyResponse.json(); + + if (!proxyResponse.ok || !tokenResponse.success) { + console.error("❌ [Callback] 通过内部代理获取访问令牌失败:", tokenResponse); + return redirect("/login?error=token_proxy_error"); } + + // --- 修改结束 --- + + console.log("✅ [Callback] 访问令牌获取成功"); + + // --- 修改开始: 通过内部代理获取用户信息 --- + console.log(`🔧 [Callback] 开始通过内部代理获取用户信息... (目标: ${origin}/api/oauth/userinfo)`); + const userInfoProxyResponse = await fetch(`${origin}/api/oauth/userinfo`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ accessToken: tokenResponse.access_token }), + }); + + const userInfoResponse = await userInfoProxyResponse.json(); + + if (!userInfoProxyResponse.ok || !userInfoResponse.success) { + console.error("❌ [Callback] 通过内部代理获取用户信息失败:", userInfoResponse); + return redirect("/login?error=userinfo_proxy_error"); + } + + // 将代理返回的用户信息包装成与原有一致的结构 + const userInfo = { + success: true, + data: userInfoResponse.data, + }; + // --- 修改结束 --- + + console.log("✅ [Callback] 用户信息获取成功"); // TODO 根据用户信息判断用户角色,这里可以根据实际业务逻辑调整 暂定都是common - // const userRole = userInfo.data.username === "admin" ? "developer" : "common"; const userRole = "common"; // 获取重定向URL @@ -91,7 +118,7 @@ export async function loader({ request }: LoaderFunctionArgs) { ou_id: savedUserData.ou_id, ou_name: savedUserData.ou_name, is_leader: savedUserData.is_leader, - user_role: userRole + user_role: userRole as 'common' | 'developer' }; const frontendJWT = JWTUtils.generateJWT(jwtUserInfo, tokenResponse.expires_in); diff --git a/app/routes/cross-checking._index.tsx b/app/routes/cross-checking._index.tsx index 81150b5..f7ee4d8 100644 --- a/app/routes/cross-checking._index.tsx +++ b/app/routes/cross-checking._index.tsx @@ -172,7 +172,7 @@ export async function action({ request }: ActionFunctionArgs) { }, { status: 500 }); } - // console.log('用户任务详情返回:', response.data); + console.log('用户任务详情返回:', response.data); return Response.json({ success: true, data: response.data diff --git a/app/routes/documents._index.tsx b/app/routes/documents._index.tsx index 1e0abc3..21960c9 100644 --- a/app/routes/documents._index.tsx +++ b/app/routes/documents._index.tsx @@ -192,6 +192,26 @@ export default function DocumentsIndex() { // 添加一个状态来跟踪是否执行了删除操作 const [isDeleting, setIsDeleting] = useState(false); + // 查询参数记忆 key 与保存/恢复方法 + const SEARCH_PARAMS_STORAGE_KEY = 'documents.searchParams'; + const persistSearchParams = (params: URLSearchParams) => { + if (typeof window !== 'undefined') { + sessionStorage.setItem(SEARCH_PARAMS_STORAGE_KEY, params.toString()); + } + }; + + // 首次进入且 URL 无任何查询参数时,尝试从 sessionStorage 恢复 + useEffect(() => { + if (typeof window === 'undefined') return; + const hasAnyParam = Array.from(searchParams.keys()).length > 0; + const stored = sessionStorage.getItem(SEARCH_PARAMS_STORAGE_KEY); + if (!hasAnyParam && stored) { + setSearchParams(new URLSearchParams(stored)); + } + // 仅初始化检查一次 + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + // 从URL获取当前筛选条件 const search = searchParams.get("search") || ""; const documentType = searchParams.get("documentType") || ""; @@ -202,7 +222,7 @@ export default function DocumentsIndex() { const dateTo = searchParams.get("dateTo") || ""; const currentPage = parseInt(searchParams.get("page") || "1", 10); const pageSize = parseInt(searchParams.get("pageSize") || "10", 10); - + // 客户端数据请求 const fetchData = useCallback(async (storedReviewType: string) => { setIsLoadingData(true); @@ -323,15 +343,19 @@ export default function DocumentsIndex() { // 分页处理函数 const handlePageChange = (page: number) => { - searchParams.set("page", page.toString()); - setSearchParams(searchParams); + const params = new URLSearchParams(searchParams); + params.set("page", page.toString()); + persistSearchParams(params); + setSearchParams(params); }; // 每页条数变更处理函数 const handlePageSizeChange = (size: number) => { - searchParams.set("pageSize", size.toString()); - searchParams.set("page", "1"); // 重置到第一页 - setSearchParams(searchParams); + const params = new URLSearchParams(searchParams); + params.set("pageSize", size.toString()); + params.set("page", "1"); // 重置到第一页 + persistSearchParams(params); + setSearchParams(params); }; // 处理文档名称搜索 @@ -343,6 +367,7 @@ export default function DocumentsIndex() { params.delete("search"); } params.set("page", "1"); // 重置页码 + persistSearchParams(params); setSearchParams(params); }; @@ -355,6 +380,7 @@ export default function DocumentsIndex() { params.delete("documentNumber"); } params.set("page", "1"); // 重置页码 + persistSearchParams(params); setSearchParams(params); }; @@ -367,6 +393,7 @@ export default function DocumentsIndex() { params.delete("documentType"); } params.set("page", "1"); // 重置页码 + persistSearchParams(params); setSearchParams(params); }; @@ -379,6 +406,7 @@ export default function DocumentsIndex() { params.delete("auditStatus"); } params.set("page", "1"); // 重置页码 + persistSearchParams(params); setSearchParams(params); }; @@ -391,6 +419,7 @@ export default function DocumentsIndex() { params.delete(field); } params.set("page", "1"); // 重置页码 + persistSearchParams(params); setSearchParams(params); }; @@ -419,7 +448,10 @@ export default function DocumentsIndex() { resetInput('input[name="dateFrom"]'); resetInput('input[name="dateTo"]'); - // 重置URL参数 + // 重置URL参数并清除保存 + if (typeof window !== 'undefined') { + sessionStorage.removeItem(SEARCH_PARAMS_STORAGE_KEY); + } setSearchParams(new URLSearchParams({ page: "1", pageSize: pageSize.toString() @@ -681,8 +713,10 @@ export default function DocumentsIndex() { return; } } - // console.log('更新成功,开始跳转') - // 导航到评查详情页 + // 导航到评查详情页前保存查询参数 + if (typeof window !== 'undefined') { + sessionStorage.setItem(SEARCH_PARAMS_STORAGE_KEY, searchParams.toString()); + } navigate(`/reviews?id=${fileId}&previousRoute=documents`); }; diff --git a/app/routes/rules-files.tsx b/app/routes/rules-files.tsx index a8f525f..dd70ca6 100644 --- a/app/routes/rules-files.tsx +++ b/app/routes/rules-files.tsx @@ -102,6 +102,27 @@ export default function RulesFiles() { const [isLoading, setIsLoading] = useState(true); const [reviewType, setReviewType] = useState(null); + // 保存/恢复 查询参数 的 sessionStorage key + const SEARCH_PARAMS_STORAGE_KEY = 'rulesFiles.searchParams'; + + const persistSearchParams = useCallback((params: URLSearchParams) => { + if (typeof window !== 'undefined') { + sessionStorage.setItem(SEARCH_PARAMS_STORAGE_KEY, params.toString()); + } + }, []); + + // 首次进入列表页且 URL 无查询参数时,尝试恢复上次保存的参数 + useEffect(() => { + if (typeof window === 'undefined') return; + const hasAnyParam = Array.from(searchParams.keys()).length > 0; + const stored = sessionStorage.getItem(SEARCH_PARAMS_STORAGE_KEY); + if (!hasAnyParam && stored) { + setSearchParams(new URLSearchParams(stored)); + } + // 仅在初始渲染时检查 + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + // 处理初始加载数据loader的错误 useEffect(() => { if(result === false && message) { @@ -254,6 +275,7 @@ export default function RulesFiles() { // 切换筛选条件时,重置到第一页 newParams.set('page', '1'); + persistSearchParams(newParams); setSearchParams(newParams); }; @@ -269,6 +291,7 @@ export default function RulesFiles() { // 搜索时,重置到第一页 newParams.set('page', '1'); + persistSearchParams(newParams); setSearchParams(newParams); }; @@ -276,6 +299,7 @@ export default function RulesFiles() { const handlePageChange = (page: number) => { const newParams = new URLSearchParams(searchParams); newParams.set('page', page.toString()); + persistSearchParams(newParams); setSearchParams(newParams); }; @@ -284,6 +308,7 @@ export default function RulesFiles() { const newParams = new URLSearchParams(searchParams); newParams.set('pageSize', size.toString()); newParams.set('page', '1'); // 改变每页条数时重置为第一页 + persistSearchParams(newParams); setSearchParams(newParams); }; @@ -311,6 +336,10 @@ export default function RulesFiles() { } // 导航到评查详情页 + // 在离开当前页前保存当前查询参数,返回时可恢复 + if (typeof window !== 'undefined') { + sessionStorage.setItem(SEARCH_PARAMS_STORAGE_KEY, searchParams.toString()); + } navigate(`/reviews?id=${fileId}&previousRoute=rulesFiles`); }; @@ -412,6 +441,9 @@ export default function RulesFiles() { if(searchInput) { (searchInput as HTMLInputElement).value = ''; } + if (typeof window !== 'undefined') { + sessionStorage.removeItem(SEARCH_PARAMS_STORAGE_KEY); + } setSearchParams(newParams); }; diff --git a/app/routes/rules._index.tsx b/app/routes/rules._index.tsx index a936ad2..54282c0 100644 --- a/app/routes/rules._index.tsx +++ b/app/routes/rules._index.tsx @@ -188,6 +188,26 @@ export default function RulesIndex() { // 添加一个状态来跟踪是否执行了删除操作 const [isDeleting, setIsDeleting] = useState(false); + // 查询参数记忆 key 与保存/恢复 + const SEARCH_PARAMS_STORAGE_KEY = 'rules.searchParams'; + const persistSearchParams = (params: URLSearchParams) => { + if (typeof window !== 'undefined') { + sessionStorage.setItem(SEARCH_PARAMS_STORAGE_KEY, params.toString()); + } + }; + + // 首次进入页且 URL 无参数时尝试恢复 + useEffect(() => { + if (typeof window === 'undefined') return; + const hasAnyParam = Array.from(searchParams.keys()).length > 0; + const stored = sessionStorage.getItem(SEARCH_PARAMS_STORAGE_KEY); + if (!hasAnyParam && stored) { + setSearchParams(new URLSearchParams(stored)); + } + // 仅初始化检查一次 + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + // 获取当前的ruleType值 const ruleTypeParam = searchParams.get('ruleType'); @@ -416,7 +436,7 @@ export default function RulesIndex() { // 切换筛选条件时,重置到第一页 newParams.set('page', '1'); - + persistSearchParams(newParams); setSearchParams(newParams); }; @@ -431,7 +451,7 @@ export default function RulesIndex() { // 搜索时,重置到第一页 newParams.set('page', '1'); - + persistSearchParams(newParams); setSearchParams(newParams); }; @@ -463,6 +483,7 @@ export default function RulesIndex() { const handlePageChange = (page: number) => { const newParams = new URLSearchParams(searchParams); newParams.set('page', page.toString()); + persistSearchParams(newParams); setSearchParams(newParams); }; @@ -471,6 +492,7 @@ export default function RulesIndex() { const newParams = new URLSearchParams(searchParams); newParams.set('pageSize', size.toString()); newParams.set('page', '1'); // 更改每页条数时,重置到第一页 + persistSearchParams(newParams); setSearchParams(newParams); }; @@ -482,6 +504,9 @@ export default function RulesIndex() { } // 保留reviewType的过滤条件,只重置其他条件 const newParams = new URLSearchParams(); + if (typeof window !== 'undefined') { + sessionStorage.removeItem(SEARCH_PARAMS_STORAGE_KEY); + } setSearchParams(newParams); };