fix: 1.将合同模板和交叉评查中的文件下载改用通过后端api进行转发获取文件来下载。 2.修复登录过程中token认证的代码问题。 3.完善api-config文件中不同端口号不同的回调地址配置。
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
// import { useState } from 'react';
|
// import { useState } from 'react';
|
||||||
import { useNavigate } from '@remix-run/react';
|
import { useNavigate } from '@remix-run/react';
|
||||||
|
// 导入统一的下载方法和提示服务
|
||||||
|
import { downloadFile } from '~/api/axios-client';
|
||||||
|
import { toastService } from '~/components/ui/Toast';
|
||||||
|
|
||||||
interface Template {
|
interface Template {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -29,56 +32,49 @@ export function TemplateCard({ template, onClick }: TemplateCardProps) {
|
|||||||
setIsFavorited(!isFavorited);
|
setIsFavorited(!isFavorited);
|
||||||
}; */
|
}; */
|
||||||
|
|
||||||
// MinIO下载URL构建函数
|
// 使用统一的下载方法(与 rules-files.tsx 相同)
|
||||||
const buildDownloadUrl = (filePath: string): string => {
|
const handleDownloadFile = async (filePath: string, fileName: string) => {
|
||||||
// 使用实际的MinIO配置
|
|
||||||
const minioHost = 'http://nas.7bm.co:9000';
|
|
||||||
const bucketName = 'docauditai';
|
|
||||||
|
|
||||||
// 确保文件路径不以/开头
|
|
||||||
const cleanPath = filePath.startsWith('/') ? filePath.substring(1) : filePath;
|
|
||||||
|
|
||||||
return `${minioHost}/${bucketName}/${cleanPath}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 下载文件函数
|
|
||||||
const downloadFile = async (filePath: string, fileName: string) => {
|
|
||||||
try {
|
try {
|
||||||
const downloadUrl = buildDownloadUrl(filePath);
|
// 使用axios封装的下载方法
|
||||||
|
const blob = await downloadFile(filePath);
|
||||||
|
|
||||||
|
// 创建Blob URL
|
||||||
|
const blobUrl = URL.createObjectURL(blob);
|
||||||
|
|
||||||
// 清理文件名,移除可能导致问题的字符
|
// 清理文件名,移除可能导致问题的字符
|
||||||
const cleanFileName = fileName.replace(/[<>:"/\\|?*]/g, '_');
|
const cleanFileName = fileName.replace(/[<>:"/\\|?*]/g, '_');
|
||||||
|
|
||||||
// 创建临时下载链接
|
// 创建一个隐藏的a标签并点击它
|
||||||
const link = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
link.href = downloadUrl;
|
a.style.display = 'none';
|
||||||
link.download = cleanFileName;
|
a.href = blobUrl;
|
||||||
link.target = '_blank';
|
a.download = cleanFileName;
|
||||||
|
document.body.appendChild(a);
|
||||||
// 触发下载
|
a.click();
|
||||||
document.body.appendChild(link);
|
|
||||||
link.click();
|
// 清理
|
||||||
document.body.removeChild(link);
|
setTimeout(() => {
|
||||||
|
document.body.removeChild(a);
|
||||||
// console.log('开始下载文件:', cleanFileName);
|
URL.revokeObjectURL(blobUrl);
|
||||||
|
}, 100);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('下载文件失败:', error);
|
console.error('下载文件失败:', error);
|
||||||
alert('下载失败,请稍后重试');
|
toastService.error(`下载文件失败: ${error instanceof Error ? error.message : '未知错误'}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleActionClick = (e: React.MouseEvent, action: string) => {
|
const handleActionClick = (e: React.MouseEvent, action: string) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case '立即下载':
|
case '立即下载':
|
||||||
if (template.file_path) {
|
if (template.file_path) {
|
||||||
// 构建文件名,使用模板标题和文件格式
|
// 构建文件名,使用模板标题和文件格式
|
||||||
const fileExtension = template.file_format || 'docx';
|
const fileExtension = template.file_format || 'docx';
|
||||||
const fileName = `${template.title}.${fileExtension}`;
|
const fileName = `${template.title}.${fileExtension}`;
|
||||||
downloadFile(template.file_path, fileName);
|
handleDownloadFile(template.file_path, fileName);
|
||||||
} else {
|
} else {
|
||||||
alert('文件路径不存在,无法下载');
|
toastService.error('文件路径不存在,无法下载');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '预览':
|
case '预览':
|
||||||
|
|||||||
@@ -2,11 +2,15 @@
|
|||||||
* 交叉评查文件信息组件
|
* 交叉评查文件信息组件
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// 导入统一的下载方法和提示服务
|
||||||
|
import { downloadFile } from '~/api/axios-client';
|
||||||
|
import { toastService } from '~/components/ui/Toast';
|
||||||
|
|
||||||
interface FileInfoProps {
|
interface FileInfoProps {
|
||||||
fileInfo: {
|
fileInfo: {
|
||||||
fileName: string;
|
fileName: string;
|
||||||
contractNumber: string;
|
contractNumber: string;
|
||||||
fileSize?: string;
|
fileSize?: string;
|
||||||
fileFormat?: string;
|
fileFormat?: string;
|
||||||
pageCount?: number;
|
pageCount?: number;
|
||||||
uploadTime?: string;
|
uploadTime?: string;
|
||||||
@@ -21,17 +25,36 @@ interface FileInfoProps {
|
|||||||
|
|
||||||
export function FileInfo({ fileInfo, onConfirmResults }: FileInfoProps) {
|
export function FileInfo({ fileInfo, onConfirmResults }: FileInfoProps) {
|
||||||
|
|
||||||
const handleDownloadFile = () => {
|
// 使用统一的下载方法(与 rules-files.tsx 相同)
|
||||||
if (fileInfo.path) {
|
const handleDownloadFile = async () => {
|
||||||
// 创建一个隐藏的下载链接
|
if (!fileInfo.path) {
|
||||||
const link = document.createElement('a');
|
toastService.error('文件路径不存在,无法下载');
|
||||||
link.href = fileInfo.path;
|
return;
|
||||||
link.download = fileInfo.fileName;
|
}
|
||||||
document.body.appendChild(link);
|
|
||||||
link.click();
|
try {
|
||||||
document.body.removeChild(link);
|
// 使用axios封装的下载方法
|
||||||
} else {
|
const blob = await downloadFile(fileInfo.path);
|
||||||
alert('文件路径不存在,无法下载');
|
|
||||||
|
// 创建Blob URL
|
||||||
|
const blobUrl = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
// 创建一个隐藏的a标签并点击它
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.style.display = 'none';
|
||||||
|
a.href = blobUrl;
|
||||||
|
a.download = fileInfo.fileName || 'document';
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
|
||||||
|
// 清理
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.removeChild(a);
|
||||||
|
URL.revokeObjectURL(blobUrl);
|
||||||
|
}, 100);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('下载文件失败:', error);
|
||||||
|
toastService.error(`下载文件失败: ${error instanceof Error ? error.message : '未知错误'}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+35
-54
@@ -14,15 +14,15 @@ interface ApiConfig {
|
|||||||
// OAuth2.0配置
|
// OAuth2.0配置
|
||||||
oauth: {
|
oauth: {
|
||||||
// IDaaS服务器地址
|
// IDaaS服务器地址
|
||||||
serverUrl: string;
|
serverUrl?: string;
|
||||||
// OAuth2应用Client ID
|
// OAuth2应用Client ID
|
||||||
clientId: string;
|
clientId?: string;
|
||||||
// OAuth2应用Client Secret
|
// OAuth2应用Client Secret
|
||||||
clientSecret: string;
|
clientSecret?: string;
|
||||||
// 回调地址
|
// 回调地址
|
||||||
redirectUri: string;
|
redirectUri?: string;
|
||||||
// 应用ID(用于登出)
|
// 应用ID(用于登出)
|
||||||
appId: string;
|
appId?: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,51 +30,15 @@ interface ApiConfig {
|
|||||||
// 根据不同端口提供不同的API配置
|
// 根据不同端口提供不同的API配置
|
||||||
const portConfigs: Record<string, Partial<ApiConfig>> = {
|
const portConfigs: Record<string, Partial<ApiConfig>> = {
|
||||||
|
|
||||||
// 测试主要服务实例
|
|
||||||
'5173': {
|
|
||||||
baseUrl: 'http://172.16.0.55:8000',
|
|
||||||
documentUrl: 'http://172.16.0.55:8000/docauditai/',
|
|
||||||
uploadUrl: 'http://172.16.0.55:8000/admin/documents'
|
|
||||||
},
|
|
||||||
// 测试客户端实例
|
|
||||||
'5174': {
|
|
||||||
baseUrl: 'http://172.16.0.55:5174',
|
|
||||||
documentUrl: 'http://172.16.0.55:5174/docauditai/',
|
|
||||||
uploadUrl: 'http://172.16.0.55:5174/admin/documents'
|
|
||||||
},
|
|
||||||
// 测试客户端实例
|
|
||||||
'5175': {
|
|
||||||
baseUrl: 'http://172.16.0.55:5175',
|
|
||||||
documentUrl: 'http://172.16.0.55:5175/docauditai/',
|
|
||||||
uploadUrl: 'http://172.16.0.55:5175/admin/documents'
|
|
||||||
},
|
|
||||||
// 测试客户端实例
|
|
||||||
'5176': {
|
|
||||||
baseUrl: 'http://172.16.0.55:5176',
|
|
||||||
documentUrl: 'http://172.16.0.55:5176/docauditai/',
|
|
||||||
uploadUrl: 'http://172.16.0.55:5176/admin/documents'
|
|
||||||
},
|
|
||||||
// 测试客户端实例
|
|
||||||
'5177': {
|
|
||||||
baseUrl: 'http://172.16.0.55:5177',
|
|
||||||
documentUrl: 'http://172.16.0.55:5177/docauditai/',
|
|
||||||
uploadUrl: 'http://172.16.0.55:5177/admin/documents'
|
|
||||||
},
|
|
||||||
// 测试客户端实例
|
|
||||||
'5178': {
|
|
||||||
baseUrl: 'http://172.16.0.55:8008',
|
|
||||||
documentUrl: 'http://172.16.0.55:8008/docauditai/',
|
|
||||||
uploadUrl: 'http://172.16.0.55:8008/admin/documents'
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 主要
|
// 主要
|
||||||
// 梅州
|
// 梅州
|
||||||
'51703': {
|
'51703': {
|
||||||
baseUrl: 'http://nas.7bm.co:8073',
|
baseUrl: 'http://nas.7bm.co:8073',
|
||||||
documentUrl: 'http://nas.7bm.co:8073/docauditai/',
|
documentUrl: 'http://nas.7bm.co:8073/docauditai/',
|
||||||
uploadUrl: 'http://nas.7bm.co:8073/admin/documents'
|
uploadUrl: 'http://nas.7bm.co:8073/admin/documents',
|
||||||
|
oauth: {
|
||||||
|
redirectUri: 'http://10.79.97.17:51703/callback'
|
||||||
|
}
|
||||||
// baseUrl: 'http://nas.7bm.co:8873',
|
// baseUrl: 'http://nas.7bm.co:8873',
|
||||||
// documentUrl: 'http://nas.7bm.co:8873/docauditai/',
|
// documentUrl: 'http://nas.7bm.co:8873/docauditai/',
|
||||||
// uploadUrl: 'http://nas.7bm.co:8873/admin/documents'
|
// uploadUrl: 'http://nas.7bm.co:8873/admin/documents'
|
||||||
@@ -85,21 +49,30 @@ const portConfigs: Record<string, Partial<ApiConfig>> = {
|
|||||||
'51704': {
|
'51704': {
|
||||||
baseUrl: 'http://10.79.97.17:8001',
|
baseUrl: 'http://10.79.97.17:8001',
|
||||||
documentUrl: 'http://10.79.97.17:8001/docauditai/',
|
documentUrl: 'http://10.79.97.17:8001/docauditai/',
|
||||||
uploadUrl: 'http://10.79.97.17:8001/admin/documents'
|
uploadUrl: 'http://10.79.97.17:8001/admin/documents',
|
||||||
|
oauth: {
|
||||||
|
redirectUri: 'http://10.79.97.17:51704/callback'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 揭阳
|
// 揭阳
|
||||||
'51705': {
|
'51705': {
|
||||||
baseUrl: 'http://10.79.97.17:8002',
|
baseUrl: 'http://10.79.97.17:8002',
|
||||||
documentUrl: 'http://10.79.97.17:8002/docauditai/',
|
documentUrl: 'http://10.79.97.17:8002/docauditai/',
|
||||||
uploadUrl: 'http://10.79.97.17:8002/admin/documents'
|
uploadUrl: 'http://10.79.97.17:8002/admin/documents',
|
||||||
|
oauth: {
|
||||||
|
redirectUri: 'http://10.79.97.17:51705/callback'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 潮州
|
// 潮州
|
||||||
'51706': {
|
'51706': {
|
||||||
baseUrl: 'http://10.79.97.17:8003',
|
baseUrl: 'http://10.79.97.17:8003',
|
||||||
documentUrl: 'http://10.79.97.17:8003/docauditai/',
|
documentUrl: 'http://10.79.97.17:8003/docauditai/',
|
||||||
uploadUrl: 'http://10.79.97.17:8003/admin/documents'
|
uploadUrl: 'http://10.79.97.17:8003/admin/documents',
|
||||||
|
oauth: {
|
||||||
|
redirectUri: 'http://10.79.97.17:51706/callback'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
@@ -107,8 +80,12 @@ const portConfigs: Record<string, Partial<ApiConfig>> = {
|
|||||||
'51707': {
|
'51707': {
|
||||||
baseUrl: 'http://10.79.97.17:8004',
|
baseUrl: 'http://10.79.97.17:8004',
|
||||||
documentUrl: 'http://10.79.97.17:8004/docauditai/',
|
documentUrl: 'http://10.79.97.17:8004/docauditai/',
|
||||||
uploadUrl: 'http://10.79.97.17:8004/admin/documents'
|
uploadUrl: 'http://10.79.97.17:8004/admin/documents',
|
||||||
|
oauth: {
|
||||||
|
redirectUri: 'http://10.79.97.17:51707/callback'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
//test
|
//test
|
||||||
'51708': {
|
'51708': {
|
||||||
baseUrl: 'http://10.79.97.17:8005',
|
baseUrl: 'http://10.79.97.17:8005',
|
||||||
@@ -312,13 +289,17 @@ const getCurrentConfig = (): ApiConfig => {
|
|||||||
|
|
||||||
// 如果有端口特定配置,则合并配置
|
// 如果有端口特定配置,则合并配置
|
||||||
if (port && portConfigs[port]) {
|
if (port && portConfigs[port]) {
|
||||||
console.log(`🔧 使用端口特定配置: ${port}`, portConfigs[port]);
|
console.log(`🔧 使用端口特定配置: ${port}`);
|
||||||
|
const portConfig = portConfigs[port];
|
||||||
defaultConfig = {
|
defaultConfig = {
|
||||||
...defaultConfig,
|
...defaultConfig,
|
||||||
...portConfigs[port],
|
...portConfig,
|
||||||
// 保持oauth配置不变,只覆盖API相关配置
|
// 如果端口配置中有oauth,需要深度合并oauth配置
|
||||||
oauth: defaultConfig.oauth
|
oauth: portConfig.oauth
|
||||||
|
? { ...defaultConfig.oauth, ...portConfig.oauth }
|
||||||
|
: defaultConfig.oauth
|
||||||
};
|
};
|
||||||
|
console.log(`🔧 使用端口特定配置---深度合并后: ${JSON.stringify(defaultConfig.oauth)}`)
|
||||||
} else {
|
} else {
|
||||||
console.log(`🔧 使用环境配置: ${env}`, defaultConfig);
|
console.log(`🔧 使用环境配置: ${env}`, defaultConfig);
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-46
@@ -1,5 +1,7 @@
|
|||||||
import { type LoaderFunctionArgs, redirect } from "@remix-run/node";
|
import { type LoaderFunctionArgs, redirect } from "@remix-run/node";
|
||||||
import { createUserSession, saveUserInfo, sessionStorage } from "~/api/login/auth.server";
|
import { createUserSession, saveUserInfo, sessionStorage } from "~/api/login/auth.server";
|
||||||
|
import { OAuthClient } from "~/api/login/oauth-client";
|
||||||
|
import { OAUTH_CONFIG } from "~/config/api-config";
|
||||||
import { JWTUtils, type UserInfoForJWT } from "~/utils/jwt";
|
import { JWTUtils, type UserInfoForJWT } from "~/utils/jwt";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,7 +41,7 @@ async function redirectToLoginWithError(request: Request, errorMessage: string)
|
|||||||
|
|
||||||
export async function loader({ request }: LoaderFunctionArgs) {
|
export async function loader({ request }: LoaderFunctionArgs) {
|
||||||
const url = new URL(request.url);
|
const url = new URL(request.url);
|
||||||
const origin = url.origin; // 获取请求的源 (e.g., "http://10.79.97.17:51703")
|
// const origin = url.origin; // 获取请求的源 (e.g., "http://10.79.97.17:51703")
|
||||||
const port = url.port; // 获取端口号
|
const port = url.port; // 获取端口号
|
||||||
const area = getAreaByPort(port); // 根据端口号获取地区
|
const area = getAreaByPort(port); // 根据端口号获取地区
|
||||||
const code = url.searchParams.get("code");
|
const code = url.searchParams.get("code");
|
||||||
@@ -80,55 +82,25 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
|||||||
try {
|
try {
|
||||||
console.log("🔧 开始处理OAuth2.0回调");
|
console.log("🔧 开始处理OAuth2.0回调");
|
||||||
|
|
||||||
// --- 修改开始: 不再直接调用OAuthClient,而是通过内部代理API ---
|
const oauthClient = new OAuthClient(OAUTH_CONFIG)
|
||||||
|
|
||||||
// 获取访问令牌 (通过代理)
|
// 开始获取访问令牌
|
||||||
console.log(`🔧 [Callback] 开始通过内部代理获取访问令牌... (目标: ${origin}/api/oauth/token)`);
|
const tokenResponse = await oauthClient.getAccessToken(code);
|
||||||
const proxyResponse = await fetch(`${origin}/api/oauth/token`, {
|
if (!tokenResponse) {
|
||||||
method: 'POST',
|
console.error("获取访问令牌失败");
|
||||||
headers: {
|
return redirect("/login?error=token_error")
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ code }),
|
|
||||||
});
|
|
||||||
|
|
||||||
const tokenResponse = await proxyResponse.json();
|
|
||||||
|
|
||||||
if (!proxyResponse.ok || !tokenResponse.success) {
|
|
||||||
console.error("❌ [Callback] 通过内部代理获取访问令牌失败:", tokenResponse);
|
|
||||||
return redirectToLoginWithError(request, "获取访问令牌失败,请重新登录");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 修改结束 ---
|
|
||||||
|
|
||||||
console.log("✅ [Callback] 访问令牌获取成功");
|
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();
|
const userInfo = await oauthClient.getUserInfo(tokenResponse.access_token);
|
||||||
|
if(!userInfo || !userInfo.success){
|
||||||
if (!userInfoProxyResponse.ok || !userInfoResponse.success) {
|
console.error('获取用户信息失败:',userInfo);
|
||||||
console.error("❌ [Callback] 通过内部代理获取用户信息失败:", userInfoResponse);
|
return redirect('/login?error=userinfo_error')
|
||||||
return redirectToLoginWithError(request, "获取用户信息失败,请重新登录");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将代理返回的用户信息包装成与原有一致的结构
|
|
||||||
const userInfo = {
|
|
||||||
success: true,
|
|
||||||
data: userInfoResponse.data,
|
|
||||||
};
|
|
||||||
// --- 修改结束 ---
|
|
||||||
|
|
||||||
console.log("✅ [Callback] 用户信息获取成功");
|
console.log("✅ [Callback] 用户信息获取成功");
|
||||||
|
|
||||||
|
|
||||||
// TODO 根据用户信息判断用户角色,这里可以根据实际业务逻辑调整 暂定都是common
|
// TODO 根据用户信息判断用户角色,这里可以根据实际业务逻辑调整 暂定都是common
|
||||||
const userRole = "common";
|
const userRole = "common";
|
||||||
|
|
||||||
@@ -138,14 +110,16 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
|||||||
// 先生成一个临时 JWT
|
// 先生成一个临时 JWT
|
||||||
const tempUserInfo = {
|
const tempUserInfo = {
|
||||||
sub: userInfo.data.sub,
|
sub: userInfo.data.sub,
|
||||||
user_id: userInfo.data.user_id || "",
|
// user_id: userInfo.data.user_id || "",
|
||||||
|
user_id: "",
|
||||||
username: userInfo.data.username,
|
username: userInfo.data.username,
|
||||||
nick_name: userInfo.data.nick_name,
|
nick_name: userInfo.data.nickname,
|
||||||
email: userInfo.data.email,
|
email: userInfo.data.email,
|
||||||
phone_number: userInfo.data.phone_number,
|
phone_number: userInfo.data.phone_number,
|
||||||
ou_id: userInfo.data.ou_id,
|
ou_id: userInfo.data.ou_id,
|
||||||
ou_name: userInfo.data.ou_name,
|
ou_name: userInfo.data.ou_name,
|
||||||
is_leader: userInfo.data.is_leader,
|
// is_leader: userInfo.data.is_leader,
|
||||||
|
is_leader: false,
|
||||||
user_role: userRole as 'common' | 'developer'
|
user_role: userRole as 'common' | 'developer'
|
||||||
};
|
};
|
||||||
const tempToken = JWTUtils.generateJWT(tempUserInfo, tokenResponse.expires_in);
|
const tempToken = JWTUtils.generateJWT(tempUserInfo, tokenResponse.expires_in);
|
||||||
@@ -176,7 +150,7 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const frontendJWT = JWTUtils.generateJWT(jwtUserInfo, tokenResponse.expires_in);
|
const frontendJWT = JWTUtils.generateJWT(jwtUserInfo, tokenResponse.expires_in);
|
||||||
console.log("前端JWT已生成");
|
// console.log("前端JWT已生成");
|
||||||
|
|
||||||
// 更新userInfo以包含数据库ID、JWT,并用数据库标准字段覆盖关键属性,确保 nick_name 等存在
|
// 更新userInfo以包含数据库ID、JWT,并用数据库标准字段覆盖关键属性,确保 nick_name 等存在
|
||||||
const enhancedUserInfo = {
|
const enhancedUserInfo = {
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ import { getUserSession } from '~/api/login/auth.server';
|
|||||||
|
|
||||||
// 导入FilePreview组件
|
// 导入FilePreview组件
|
||||||
import { FilePreview } from '~/components/reviews';
|
import { FilePreview } from '~/components/reviews';
|
||||||
|
// 导入统一的下载方法和提示服务
|
||||||
|
import { downloadFile } from '~/api/axios-client';
|
||||||
|
import { toastService } from '~/components/ui/Toast';
|
||||||
|
|
||||||
export const links = () => [
|
export const links = () => [
|
||||||
{ rel: 'stylesheet', href: styles },
|
{ rel: 'stylesheet', href: styles },
|
||||||
@@ -70,45 +73,41 @@ export default function ContractTemplateDetail() {
|
|||||||
navigate(-1);
|
navigate(-1);
|
||||||
};
|
};
|
||||||
|
|
||||||
// MinIO下载URL构建函数
|
// 使用统一的下载方法(与 rules-files.tsx 相同)
|
||||||
const buildDownloadUrl = (filePath: string): string => {
|
const handleDownload = async () => {
|
||||||
const minioHost = 'http://nas.7bm.co:9000';
|
if (!template.file_path) {
|
||||||
const bucketName = 'docauditai';
|
toastService.error('文件路径不存在,无法下载');
|
||||||
|
return;
|
||||||
const cleanPath = filePath.startsWith('/') ? filePath.substring(1) : filePath;
|
|
||||||
return `${minioHost}/${bucketName}/${cleanPath}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 下载文件函数
|
|
||||||
const downloadFile = async (filePath: string, fileName: string) => {
|
|
||||||
try {
|
|
||||||
const downloadUrl = buildDownloadUrl(filePath);
|
|
||||||
|
|
||||||
const cleanFileName = fileName.replace(/[<>:"/\\|?*]/g, '_');
|
|
||||||
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.href = downloadUrl;
|
|
||||||
link.download = cleanFileName;
|
|
||||||
link.target = '_blank';
|
|
||||||
|
|
||||||
document.body.appendChild(link);
|
|
||||||
link.click();
|
|
||||||
document.body.removeChild(link);
|
|
||||||
|
|
||||||
// console.log('开始下载文件:', cleanFileName);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('下载文件失败:', error);
|
|
||||||
alert('下载失败,请稍后重试');
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const handleDownload = () => {
|
try {
|
||||||
if (template.file_path) {
|
// 使用axios封装的下载方法
|
||||||
|
const blob = await downloadFile(template.file_path);
|
||||||
|
|
||||||
|
// 创建Blob URL
|
||||||
|
const blobUrl = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
// 清理文件名,移除可能导致问题的字符
|
||||||
const fileExtension = template.file_format || 'docx';
|
const fileExtension = template.file_format || 'docx';
|
||||||
const fileName = `${template.title}.${fileExtension}`;
|
const fileName = `${template.title}.${fileExtension}`;
|
||||||
downloadFile(template.file_path, fileName);
|
const cleanFileName = fileName.replace(/[<>:"/\\|?*]/g, '_');
|
||||||
} else {
|
|
||||||
alert('文件路径不存在,无法下载');
|
// 创建一个隐藏的a标签并点击它
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.style.display = 'none';
|
||||||
|
a.href = blobUrl;
|
||||||
|
a.download = cleanFileName;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
|
||||||
|
// 清理
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.removeChild(a);
|
||||||
|
URL.revokeObjectURL(blobUrl);
|
||||||
|
}, 100);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('下载文件失败:', error);
|
||||||
|
toastService.error(`下载文件失败: ${error instanceof Error ? error.message : '未知错误'}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user