temp:临时备份,测试合并兼容性
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Collabora 配置生成 API 路由
|
||||
*
|
||||
* 功能:
|
||||
* - 生成 Collabora iframe URL
|
||||
* - 生成 WOPI access token
|
||||
* - 返回完整的 Collabora 配置
|
||||
*
|
||||
* @encoding UTF-8
|
||||
*/
|
||||
|
||||
import { type LoaderFunctionArgs, json } from '@remix-run/node';
|
||||
import { getUserSession } from '~/api/login/auth.server';
|
||||
import { generateCollaboraConfig } from '~/lib/collabora/config.server';
|
||||
|
||||
/**
|
||||
* GET /api/collabora/config
|
||||
*
|
||||
* 查询参数:
|
||||
* - fileId: 文件路径(例如:contracts/test.docx)
|
||||
* - mode: 模式(view 或 edit),默认 view
|
||||
* - userId: 用户 ID(可选,从 session 获取)
|
||||
* - userName: 用户名(可选,从 session 获取)
|
||||
*/
|
||||
export async function loader({ request }: LoaderFunctionArgs) {
|
||||
try {
|
||||
// 获取用户会话信息
|
||||
const { userInfo } = await getUserSession(request);
|
||||
|
||||
// 解析查询参数
|
||||
const url = new URL(request.url);
|
||||
const fileId = url.searchParams.get('fileId');
|
||||
const mode = (url.searchParams.get('mode') || 'view') as 'view' | 'edit';
|
||||
const userId = url.searchParams.get('userId') || userInfo?.sub || 'guest';
|
||||
const userName = url.searchParams.get('userName') || userInfo?.nick_name || '访客';
|
||||
|
||||
// 验证必需参数
|
||||
if (!fileId) {
|
||||
return json(
|
||||
{ error: '文件路径不能为空' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// 生成 Collabora 配置
|
||||
const config = await generateCollaboraConfig({
|
||||
fileId,
|
||||
mode,
|
||||
userId,
|
||||
userName,
|
||||
});
|
||||
|
||||
return json(config);
|
||||
} catch (error) {
|
||||
console.error('生成 Collabora 配置失败:', error);
|
||||
return json(
|
||||
{
|
||||
error: error instanceof Error ? error.message : '生成配置失败',
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* WOPI 协议 API 路由
|
||||
*
|
||||
* 功能:
|
||||
* - CheckFileInfo: GET /api/collabora/wopi/files/{fileId}
|
||||
* - GetFile: GET /api/collabora/wopi/files/{fileId}/contents
|
||||
* - PutFile: POST /api/collabora/wopi/files/{fileId}/contents
|
||||
*
|
||||
* @encoding UTF-8
|
||||
*/
|
||||
|
||||
import { type LoaderFunctionArgs, type ActionFunctionArgs } from '@remix-run/node';
|
||||
import { WopiService } from '~/lib/collabora/wopi.server';
|
||||
|
||||
const wopiService = new WopiService();
|
||||
|
||||
/**
|
||||
* GET 请求处理
|
||||
* - 无 /contents 后缀 → CheckFileInfo
|
||||
* - 有 /contents 后缀 → GetFile
|
||||
*/
|
||||
export async function loader({ request, params }: LoaderFunctionArgs) {
|
||||
try {
|
||||
const url = new URL(request.url);
|
||||
const accessToken = url.searchParams.get('access_token');
|
||||
|
||||
if (!accessToken) {
|
||||
return new Response('访问令牌缺失', { status: 401 });
|
||||
}
|
||||
|
||||
// 获取文件 ID
|
||||
const fileId = params.fileId || '';
|
||||
|
||||
// 判断是否是 GetFile 请求(路径以 /contents 结尾)
|
||||
const isContentsRequest = url.pathname.endsWith('/contents');
|
||||
|
||||
if (isContentsRequest) {
|
||||
// GetFile: 返回文件内容
|
||||
const { buffer, metadata } = await wopiService.getFile(fileId, accessToken);
|
||||
|
||||
return new Response(buffer, {
|
||||
headers: {
|
||||
'Content-Type': metadata.contentType,
|
||||
'Content-Length': metadata.size.toString(),
|
||||
'Content-Disposition': 'inline',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// CheckFileInfo: 返回文件元数据
|
||||
const checkFileInfo = await wopiService.checkFileInfo(fileId, accessToken);
|
||||
|
||||
// 注意:CheckFileInfo 必须返回纯 JSON,不能使用 Result.success() 包装
|
||||
return Response.json(checkFileInfo);
|
||||
} catch (error) {
|
||||
console.error('WOPI GET 失败:', error);
|
||||
return new Response(
|
||||
error instanceof Error ? error.message : 'Internal server error',
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* POST 请求处理
|
||||
* - PutFile: 保存文件内容
|
||||
*/
|
||||
export async function action({ request, params }: ActionFunctionArgs) {
|
||||
try {
|
||||
const url = new URL(request.url);
|
||||
const accessToken = url.searchParams.get('access_token');
|
||||
|
||||
if (!accessToken) {
|
||||
return new Response('访问令牌缺失', { status: 401 });
|
||||
}
|
||||
|
||||
const fileId = params.fileId || '';
|
||||
|
||||
// 判断是否是 PutFile 请求(路径以 /contents 结尾)
|
||||
const isContentsRequest = url.pathname.endsWith('/contents');
|
||||
|
||||
if (!isContentsRequest) {
|
||||
return new Response('PutFile 必须使用 /contents 路径', { status: 400 });
|
||||
}
|
||||
|
||||
// PutFile: 保存文件
|
||||
const fileBuffer = await request.arrayBuffer();
|
||||
await wopiService.putFile(fileId, accessToken, fileBuffer);
|
||||
|
||||
return new Response(null, { status: 200 });
|
||||
} catch (error) {
|
||||
console.error('WOPI POST 失败:', error);
|
||||
return new Response(
|
||||
error instanceof Error ? error.message : 'Internal server error',
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -150,11 +150,13 @@ export default function ContractTemplateDetail() {
|
||||
}; */
|
||||
|
||||
// 创建文件内容对象用于FilePreview组件
|
||||
const fileContent = template.pdf_file_path ? {
|
||||
// 优先使用原始文件路径(支持docx),如果没有则使用pdf_file_path
|
||||
const previewPath = template.file_path || template.pdf_file_path;
|
||||
const fileContent = previewPath ? {
|
||||
title: template.title,
|
||||
contractNumber: template.template_code,
|
||||
// 使用pdf_file_path字段
|
||||
path: template.pdf_file_path,
|
||||
// 使用file_path以支持多种格式(docx/pdf)
|
||||
path: previewPath,
|
||||
parties: {
|
||||
partyA: {
|
||||
name: '',
|
||||
|
||||
Reference in New Issue
Block a user