新增文档列表的修改页面
This commit is contained in:
+109
-1
@@ -1,4 +1,4 @@
|
|||||||
import { postgrestGet, postgrestDelete, type PostgrestParams } from '../postgrest-client';
|
import { postgrestGet, postgrestDelete, postgrestPut, type PostgrestParams } from '../postgrest-client';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { getDocumentTypes } from '../document-types/document-types';
|
import { getDocumentTypes } from '../document-types/document-types';
|
||||||
|
|
||||||
@@ -315,4 +315,112 @@ export async function getDocument(id: string): Promise<{
|
|||||||
status: 500
|
status: 500
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件下载链接
|
||||||
|
* @param filePath 文件路径
|
||||||
|
* @returns 下载链接
|
||||||
|
*/
|
||||||
|
export async function getFileDownloadUrl(filePath: string): Promise<{
|
||||||
|
data?: { downloadUrl: string };
|
||||||
|
error?: string;
|
||||||
|
status?: number;
|
||||||
|
}> {
|
||||||
|
try {
|
||||||
|
if (!filePath) {
|
||||||
|
return { error: '文件路径不能为空', status: 400 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建API请求参数
|
||||||
|
const params: PostgrestParams = {
|
||||||
|
filter: {
|
||||||
|
'path': `eq.${filePath}`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 这里应该调用获取文件下载链接的API
|
||||||
|
// 假设后端有这样的端点:/api/files/generate-download-url?path=xxx
|
||||||
|
// 实际项目中需要根据你的后端API调整
|
||||||
|
|
||||||
|
// 临时解决方案:返回Remix路由路径
|
||||||
|
// 这将通过Remix服务器代理对文件的访问
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
downloadUrl: `/documents/download?path=${encodeURIComponent(filePath)}`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取文件下载链接失败:', error);
|
||||||
|
return {
|
||||||
|
error: error instanceof Error ? error.message : '获取文件下载链接失败',
|
||||||
|
status: 500
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新文档信息
|
||||||
|
* @param id 文档ID
|
||||||
|
* @param document 部分文档数据
|
||||||
|
* @returns 更新结果
|
||||||
|
*/
|
||||||
|
export async function updateDocument(id: string, document: Partial<DocumentUI> & { remark?: string }): Promise<{
|
||||||
|
data?: DocumentUI;
|
||||||
|
error?: string;
|
||||||
|
status?: number;
|
||||||
|
}> {
|
||||||
|
try {
|
||||||
|
if (!id) {
|
||||||
|
return { error: '文档ID不能为空', status: 400 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 准备API数据 - 将UI数据转换为API格式
|
||||||
|
const apiDocument: Partial<Document> = {};
|
||||||
|
|
||||||
|
if (document.documentNumber !== undefined) {
|
||||||
|
apiDocument.document_number = document.documentNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.type !== undefined) {
|
||||||
|
apiDocument.type_id = parseInt(document.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.status !== undefined) {
|
||||||
|
apiDocument.status = document.status as 'pass' | 'warning' | 'waiting' | 'processing' | 'fail';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.isTest !== undefined) {
|
||||||
|
apiDocument.is_test_document = document.isTest;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.remark !== undefined) {
|
||||||
|
apiDocument.remark = document.remark;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('更新文档API数据:', apiDocument);
|
||||||
|
|
||||||
|
const response = await postgrestPut<Document, Partial<Document>>(
|
||||||
|
'documents',
|
||||||
|
apiDocument,
|
||||||
|
{ id: parseInt(id) }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.error) {
|
||||||
|
console.error('更新文档API错误:', response.error);
|
||||||
|
return { error: response.error, status: response.status };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取更新后的完整文档数据
|
||||||
|
const updatedResponse = await getDocument(id);
|
||||||
|
|
||||||
|
return updatedResponse;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('更新文档信息失败:', error);
|
||||||
|
return {
|
||||||
|
error: error instanceof Error ? error.message : '更新文档信息失败',
|
||||||
|
status: 500
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -10,7 +10,7 @@ import { FileTypeTag } from "~/components/ui/FileTypeTag";
|
|||||||
import { FileTag } from "~/components/ui/FileTag";
|
import { FileTag } from "~/components/ui/FileTag";
|
||||||
import { FilterPanel, FilterSelect, SearchFilter, DateRangeFilter } from "~/components/ui/FilterPanel";
|
import { FilterPanel, FilterSelect, SearchFilter, DateRangeFilter } from "~/components/ui/FilterPanel";
|
||||||
import documentsIndexStyles from "~/styles/pages/documents_index.css?url";
|
import documentsIndexStyles from "~/styles/pages/documents_index.css?url";
|
||||||
import { getDocuments, deleteDocument, type DocumentUI } from "~/api/files/documents";
|
import { getDocuments, deleteDocument, type DocumentUI, getFileDownloadUrl } from "~/api/files/documents";
|
||||||
import { getDocumentTypes } from "~/api/document-types/document-types";
|
import { getDocumentTypes } from "~/api/document-types/document-types";
|
||||||
|
|
||||||
// 导入样式
|
// 导入样式
|
||||||
@@ -288,15 +288,30 @@ export default function DocumentsIndex() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 下载文档
|
// 下载文档
|
||||||
const handleDownload = (path: string, fileName: string) => {
|
const handleDownload = async (path: string, fileName: string) => {
|
||||||
console.log('handleDownload',path,fileName)
|
console.log('handleDownload',path,fileName)
|
||||||
// 创建一个隐藏的a标签并点击它
|
try {
|
||||||
const a = document.createElement('a');
|
// 使用API获取授权的下载链接
|
||||||
a.href = path;
|
// const { data, error } = await getFileDownloadUrl(path);
|
||||||
a.download = fileName; // 设置下载的文件名
|
|
||||||
document.body.appendChild(a);
|
// if (error || !data?.downloadUrl) {
|
||||||
a.click();
|
// console.error('获取下载链接失败:', error);
|
||||||
document.body.removeChild(a);
|
// alert('获取下载链接失败: ' + (error || '未知错误'));
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 创建一个隐藏的a标签并点击它
|
||||||
|
const a = document.createElement('a');
|
||||||
|
// a.href = data.downloadUrl;
|
||||||
|
a.href = path;
|
||||||
|
a.download = fileName; // 设置下载的文件名
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('下载文件失败:', err);
|
||||||
|
alert('下载文件失败: ' + (err instanceof Error ? err.message : '未知错误'));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 删除文档
|
// 删除文档
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { LoaderFunctionArgs } from "@remix-run/node";
|
||||||
|
import { postgrestGet } from "~/api/postgrest-client";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文档下载路由 - 处理文档下载请求
|
||||||
|
* 通过重定向到带有授权的连接来允许下载文件
|
||||||
|
*/
|
||||||
|
export async function loader({ request }: LoaderFunctionArgs) {
|
||||||
|
try {
|
||||||
|
// 获取文件路径参数
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const filePath = url.searchParams.get("path");
|
||||||
|
|
||||||
|
if (!filePath) {
|
||||||
|
return new Response("缺少文件路径参数", { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用Minio API获取带有授权的预签名URL
|
||||||
|
// 这里假设后端有一个生成预签名URL的API
|
||||||
|
const response = await postgrestGet<{ presignedUrl: string }>(
|
||||||
|
'/minio/presign',
|
||||||
|
{
|
||||||
|
filter: {
|
||||||
|
'object_path': `eq.${filePath}`,
|
||||||
|
'expires_in': 'eq.300' // 5分钟有效期
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.error) {
|
||||||
|
console.error("获取文件下载链接失败:", response.error);
|
||||||
|
return new Response("获取文件下载链接失败", { status: 500 });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.data?.presignedUrl) {
|
||||||
|
return new Response("无法获取文件下载链接", { status: 404 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重定向到预签名URL,这样浏览器就能直接下载文件
|
||||||
|
return Response.redirect(response.data.presignedUrl);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("文件下载处理失败:", error);
|
||||||
|
return new Response(
|
||||||
|
"文件下载处理失败: " + (error instanceof Error ? error.message : "未知错误"),
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+109
-148
@@ -4,6 +4,9 @@ import { redirect, type ActionFunctionArgs, type LoaderFunctionArgs, type MetaFu
|
|||||||
import { Card } from "~/components/ui/Card";
|
import { Card } from "~/components/ui/Card";
|
||||||
import { Button } from "~/components/ui/Button";
|
import { Button } from "~/components/ui/Button";
|
||||||
import documentEditStyles from "~/styles/pages/documents_edit.css?url";
|
import documentEditStyles from "~/styles/pages/documents_edit.css?url";
|
||||||
|
import { getDocument, updateDocument } from "~/api/files/documents";
|
||||||
|
import { getDocumentTypes } from "~/api/document-types/document-types";
|
||||||
|
import { FileTag } from "~/components/ui/FileTag";
|
||||||
|
|
||||||
export function links() {
|
export function links() {
|
||||||
return [{ rel: "stylesheet", href: documentEditStyles }];
|
return [{ rel: "stylesheet", href: documentEditStyles }];
|
||||||
@@ -18,7 +21,7 @@ export const meta: MetaFunction = () => {
|
|||||||
|
|
||||||
// 文档状态定义
|
// 文档状态定义
|
||||||
enum DocumentStatus {
|
enum DocumentStatus {
|
||||||
PENDING = "pending",
|
WAITING = "waiting",
|
||||||
PROCESSING = "processing",
|
PROCESSING = "processing",
|
||||||
PASS = "pass",
|
PASS = "pass",
|
||||||
WARNING = "warning",
|
WARNING = "warning",
|
||||||
@@ -27,124 +30,13 @@ enum DocumentStatus {
|
|||||||
|
|
||||||
// 文档状态对应的中文标签
|
// 文档状态对应的中文标签
|
||||||
const STATUS_LABELS: Record<DocumentStatus, string> = {
|
const STATUS_LABELS: Record<DocumentStatus, string> = {
|
||||||
[DocumentStatus.PENDING]: "待审核",
|
[DocumentStatus.WAITING]: "待审核",
|
||||||
[DocumentStatus.PROCESSING]: "审核中",
|
[DocumentStatus.PROCESSING]: "审核中",
|
||||||
[DocumentStatus.PASS]: "通过",
|
[DocumentStatus.PASS]: "通过",
|
||||||
[DocumentStatus.WARNING]: "警告",
|
[DocumentStatus.WARNING]: "警告",
|
||||||
[DocumentStatus.FAIL]: "不通过"
|
[DocumentStatus.FAIL]: "不通过"
|
||||||
};
|
};
|
||||||
|
|
||||||
// 文档类型接口
|
|
||||||
interface DocumentType {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 历史记录项接口
|
|
||||||
interface HistoryItem {
|
|
||||||
time: string;
|
|
||||||
user: string;
|
|
||||||
action: string;
|
|
||||||
details: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 文档接口
|
|
||||||
interface Document {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
type_id: string;
|
|
||||||
document_number: string | null;
|
|
||||||
file_size: number;
|
|
||||||
upload_time: string;
|
|
||||||
is_test_document: boolean;
|
|
||||||
status: DocumentStatus;
|
|
||||||
remark: string | null;
|
|
||||||
history: HistoryItem[];
|
|
||||||
file_url?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 模拟API获取文档类型列表
|
|
||||||
async function getDocumentTypes(): Promise<DocumentType[]> {
|
|
||||||
// 这里应该是实际API调用
|
|
||||||
return [
|
|
||||||
{ id: "1", name: "销售合同" },
|
|
||||||
{ id: "2", name: "采购合同" },
|
|
||||||
{ id: "3", name: "专卖许可证" },
|
|
||||||
{ id: "4", name: "行政处罚决定书" },
|
|
||||||
{ id: "5", name: "承包协议" }
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 模拟API获取文档详情
|
|
||||||
async function getDocument(id: string): Promise<Document> {
|
|
||||||
// 这里应该是实际API调用
|
|
||||||
return {
|
|
||||||
id,
|
|
||||||
name: "2023年度烟草销售框架合同.pdf",
|
|
||||||
type_id: "1", // 销售合同
|
|
||||||
document_number: "XS20230001",
|
|
||||||
file_size: 2.5 * 1024 * 1024, // 2.5MB
|
|
||||||
upload_time: "2023-10-15 15:30",
|
|
||||||
is_test_document: false,
|
|
||||||
status: DocumentStatus.PASS,
|
|
||||||
remark: "此合同为2023年度与XX公司的销售框架协议,适用于全年的烟草销售业务。",
|
|
||||||
history: [
|
|
||||||
{
|
|
||||||
time: "2023-10-15 15:30",
|
|
||||||
user: "系统",
|
|
||||||
action: "创建了此文档",
|
|
||||||
details: "首次上传文档,文档类型:销售合同,状态:待审核"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
time: "2023-10-15 16:45",
|
|
||||||
user: "张三",
|
|
||||||
action: "启动了文档审核",
|
|
||||||
details: "状态由'待审核'变更为'审核中'"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
time: "2023-10-15 17:20",
|
|
||||||
user: "系统",
|
|
||||||
action: "完成了文档审核",
|
|
||||||
details: "状态由'审核中'变更为'通过',未发现问题"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
time: "2023-10-16 09:10",
|
|
||||||
user: "李四",
|
|
||||||
action: "修改了文档属性",
|
|
||||||
details: "添加了备注信息,完善了文档编号"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
file_url: "/mock/documents/sample.pdf"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 模拟API更新文档信息
|
|
||||||
async function updateDocument(id: string, data: Partial<Document>): Promise<Document> {
|
|
||||||
// 这里应该是实际API调用
|
|
||||||
console.log("更新文档:", id, data);
|
|
||||||
|
|
||||||
// 模拟获取原始数据
|
|
||||||
const document = await getDocument(id);
|
|
||||||
|
|
||||||
// 合并更新的数据
|
|
||||||
const updatedDocument = {
|
|
||||||
...document,
|
|
||||||
...data,
|
|
||||||
// 添加新的历史记录
|
|
||||||
history: [
|
|
||||||
{
|
|
||||||
time: new Date().toISOString().replace("T", " ").slice(0, 16),
|
|
||||||
user: "当前用户",
|
|
||||||
action: "修改了文档信息",
|
|
||||||
details: `更新了文档类型、状态和备注信息`
|
|
||||||
},
|
|
||||||
...document.history
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
return updatedDocument;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 格式化文件大小
|
// 格式化文件大小
|
||||||
function formatFileSize(bytes: number): string {
|
function formatFileSize(bytes: number): string {
|
||||||
if (bytes === 0) return "0 Bytes";
|
if (bytes === 0) return "0 Bytes";
|
||||||
@@ -168,12 +60,23 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 并行获取文档详情和文档类型列表
|
// 并行获取文档详情和文档类型列表
|
||||||
const [document, documentTypes] = await Promise.all([
|
const [documentResponse, documentTypesResponse] = await Promise.all([
|
||||||
getDocument(id),
|
getDocument(id),
|
||||||
getDocumentTypes()
|
getDocumentTypes()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return Response.json({ document, documentTypes });
|
if (documentResponse.error) {
|
||||||
|
throw new Response(documentResponse.error, { status: documentResponse.status || 404 });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (documentTypesResponse.error) {
|
||||||
|
console.error("获取文档类型列表失败:", documentTypesResponse.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response.json({
|
||||||
|
document: documentResponse.data,
|
||||||
|
documentTypes: documentTypesResponse.data?.types || []
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("加载文档数据失败:", error);
|
console.error("加载文档数据失败:", error);
|
||||||
throw new Response("加载文档数据失败", { status: 500 });
|
throw new Response("加载文档数据失败", { status: 500 });
|
||||||
@@ -194,19 +97,19 @@ export async function action({ request }: ActionFunctionArgs) {
|
|||||||
const formData = await request.formData();
|
const formData = await request.formData();
|
||||||
|
|
||||||
// 从表单数据中提取字段
|
// 从表单数据中提取字段
|
||||||
const type_id = formData.get("type_id") as string;
|
const type = formData.get("type_id") as string;
|
||||||
const document_number = formData.get("document_number") as string;
|
const documentNumber = formData.get("document_number") as string;
|
||||||
const status = formData.get("status") as DocumentStatus;
|
const status = formData.get("status") as DocumentStatus;
|
||||||
const is_test_document = formData.get("is_test_document") === "on";
|
const isTest = formData.get("is_test_document") === "on";
|
||||||
const remark = formData.get("remark") as string;
|
const remark = formData.get("remark") as string;
|
||||||
|
|
||||||
// 验证必填字段
|
// 验证必填字段
|
||||||
if (!type_id || !status) {
|
if (!type || !status) {
|
||||||
return Response.json(
|
return Response.json(
|
||||||
{
|
{
|
||||||
error: "缺少必填字段",
|
error: "缺少必填字段",
|
||||||
fieldErrors: {
|
fieldErrors: {
|
||||||
type_id: !type_id ? "文档类型不能为空" : null,
|
type_id: !type ? "文档类型不能为空" : null,
|
||||||
status: !status ? "状态不能为空" : null
|
status: !status ? "状态不能为空" : null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -214,20 +117,33 @@ export async function action({ request }: ActionFunctionArgs) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('提交更新:', { type, documentNumber, status, isTest, remark });
|
||||||
|
|
||||||
// 更新文档
|
// 更新文档
|
||||||
await updateDocument(id, {
|
const updateResponse = await updateDocument(id, {
|
||||||
type_id,
|
type,
|
||||||
document_number: document_number || null,
|
documentNumber,
|
||||||
status,
|
status,
|
||||||
is_test_document,
|
isTest,
|
||||||
remark: remark || null
|
remark
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (updateResponse.error) {
|
||||||
|
console.error('更新文档失败:', updateResponse.error);
|
||||||
|
return Response.json({
|
||||||
|
error: updateResponse.error,
|
||||||
|
message: "更新文档失败,请检查提交的数据是否正确"
|
||||||
|
}, { status: updateResponse.status || 500 });
|
||||||
|
}
|
||||||
|
|
||||||
// 重定向回文档列表
|
// 重定向回文档列表
|
||||||
return redirect("/documents");
|
return redirect("/documents");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("更新文档失败:", error);
|
console.error("更新文档失败:", error);
|
||||||
return Response.json({ error: "更新文档失败" }, { status: 500 });
|
return Response.json({
|
||||||
|
error: "更新文档失败",
|
||||||
|
message: error instanceof Error ? error.message : "发生未知错误"
|
||||||
|
}, { status: 500 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,8 +153,14 @@ export default function DocumentEdit() {
|
|||||||
const actionData = useActionData<typeof action>();
|
const actionData = useActionData<typeof action>();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
// 定义类型
|
||||||
|
interface DocType {
|
||||||
|
id: string | number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
// 状态
|
// 状态
|
||||||
const [localStatus, setLocalStatus] = useState<DocumentStatus>(document.status);
|
const [localStatus, setLocalStatus] = useState<DocumentStatus>(document.status as DocumentStatus);
|
||||||
|
|
||||||
// 处理状态变更
|
// 处理状态变更
|
||||||
const handleStatusChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
const handleStatusChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||||
@@ -247,23 +169,31 @@ export default function DocumentEdit() {
|
|||||||
|
|
||||||
// 获取文档类型名称
|
// 获取文档类型名称
|
||||||
const getDocumentTypeName = (typeId: string): string => {
|
const getDocumentTypeName = (typeId: string): string => {
|
||||||
const docType = documentTypes.find((type: DocumentType) => type.id === typeId);
|
const docType = documentTypes.find((type) => (type as any).id.toString() === typeId);
|
||||||
return docType ? docType.name : "未知类型";
|
return docType ? (docType as any).name : "未知类型";
|
||||||
};
|
};
|
||||||
|
|
||||||
// 渲染状态徽章
|
// 渲染状态徽章
|
||||||
const renderStatusBadge = (status: DocumentStatus) => {
|
const renderStatusBadge = (status: string) => {
|
||||||
const statusClasses: Record<DocumentStatus, string> = {
|
const statusClasses: Record<string, string> = {
|
||||||
[DocumentStatus.PENDING]: "status-badge status-pending",
|
"waiting": "status-badge status-pending",
|
||||||
[DocumentStatus.PROCESSING]: "status-badge status-processing",
|
"processing": "status-badge status-processing",
|
||||||
[DocumentStatus.PASS]: "status-badge status-pass",
|
"pass": "status-badge status-pass",
|
||||||
[DocumentStatus.WARNING]: "status-badge status-warning",
|
"warning": "status-badge status-warning",
|
||||||
[DocumentStatus.FAIL]: "status-badge status-fail"
|
"fail": "status-badge status-fail"
|
||||||
|
};
|
||||||
|
|
||||||
|
const statusLabel: Record<string, string> = {
|
||||||
|
"waiting": "待审核",
|
||||||
|
"processing": "审核中",
|
||||||
|
"pass": "通过",
|
||||||
|
"warning": "警告",
|
||||||
|
"fail": "不通过"
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className={statusClasses[status]}>
|
<span className={statusClasses[status] || "status-badge"}>
|
||||||
{STATUS_LABELS[status]}
|
{statusLabel[status] || status}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -296,22 +226,28 @@ export default function DocumentEdit() {
|
|||||||
<Card className="mb-4">
|
<Card className="mb-4">
|
||||||
<div className="document-info">
|
<div className="document-info">
|
||||||
<div className="document-icon">
|
<div className="document-icon">
|
||||||
<i className="ri-file-pdf-line text-red-500"></i>
|
<FileTag
|
||||||
|
extension={document.fileType}
|
||||||
|
showIcon={true}
|
||||||
|
showText={false}
|
||||||
|
showBackground={false}
|
||||||
|
size="lg"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="document-details">
|
<div className="document-details">
|
||||||
<div className="document-name">{document.name}</div>
|
<div className="document-name">{document.name}</div>
|
||||||
<div className="document-meta">
|
<div className="document-meta">
|
||||||
<div className="meta-item">
|
<div className="meta-item">
|
||||||
<i className="ri-file-list-line"></i>
|
<i className="ri-file-list-line"></i>
|
||||||
<span>{getDocumentTypeName(document.type_id)}</span>
|
<span>{getDocumentTypeName(document.type)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="meta-item">
|
<div className="meta-item">
|
||||||
<i className="ri-time-line"></i>
|
<i className="ri-time-line"></i>
|
||||||
<span>{document.upload_time}</span>
|
<span>{document.uploadTime}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="meta-item">
|
<div className="meta-item">
|
||||||
<i className="ri-hard-drive-line"></i>
|
<i className="ri-hard-drive-line"></i>
|
||||||
<span>{formatFileSize(document.file_size)}</span>
|
<span>{formatFileSize(document.size)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="meta-item">
|
<div className="meta-item">
|
||||||
{renderStatusBadge(document.status)}
|
{renderStatusBadge(document.status)}
|
||||||
@@ -332,10 +268,10 @@ export default function DocumentEdit() {
|
|||||||
id="type-id"
|
id="type-id"
|
||||||
name="type_id"
|
name="type_id"
|
||||||
className="form-select"
|
className="form-select"
|
||||||
defaultValue={document.type_id}
|
defaultValue={document.type}
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
{documentTypes.map((type: DocumentType) => (
|
{documentTypes.map(type => (
|
||||||
<option key={type.id} value={type.id}>{type.name}</option>
|
<option key={type.id} value={type.id}>{type.name}</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
@@ -353,7 +289,7 @@ export default function DocumentEdit() {
|
|||||||
name="document_number"
|
name="document_number"
|
||||||
className="form-input"
|
className="form-input"
|
||||||
placeholder="请输入合同编号、许可证号等"
|
placeholder="请输入合同编号、许可证号等"
|
||||||
defaultValue={document.document_number || ""}
|
defaultValue={document.documentNumber || ""}
|
||||||
/>
|
/>
|
||||||
<div className="text-sm text-secondary mt-1">如无编号可留空</div>
|
<div className="text-sm text-secondary mt-1">如无编号可留空</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -368,7 +304,7 @@ export default function DocumentEdit() {
|
|||||||
onChange={handleStatusChange}
|
onChange={handleStatusChange}
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
<option value={DocumentStatus.PENDING}>{STATUS_LABELS[DocumentStatus.PENDING]}</option>
|
<option value={DocumentStatus.WAITING}>{STATUS_LABELS[DocumentStatus.WAITING]}</option>
|
||||||
<option value={DocumentStatus.PROCESSING}>{STATUS_LABELS[DocumentStatus.PROCESSING]}</option>
|
<option value={DocumentStatus.PROCESSING}>{STATUS_LABELS[DocumentStatus.PROCESSING]}</option>
|
||||||
<option value={DocumentStatus.PASS}>{STATUS_LABELS[DocumentStatus.PASS]}</option>
|
<option value={DocumentStatus.PASS}>{STATUS_LABELS[DocumentStatus.PASS]}</option>
|
||||||
<option value={DocumentStatus.WARNING}>{STATUS_LABELS[DocumentStatus.WARNING]}</option>
|
<option value={DocumentStatus.WARNING}>{STATUS_LABELS[DocumentStatus.WARNING]}</option>
|
||||||
@@ -388,7 +324,7 @@ export default function DocumentEdit() {
|
|||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="is-test-document"
|
id="is-test-document"
|
||||||
name="is_test_document"
|
name="is_test_document"
|
||||||
defaultChecked={document.is_test_document}
|
defaultChecked={document.isTest}
|
||||||
/>
|
/>
|
||||||
<span className="slider"></span>
|
<span className="slider"></span>
|
||||||
<span className="sr-only">标记为测试文档</span>
|
<span className="sr-only">标记为测试文档</span>
|
||||||
@@ -454,7 +390,32 @@ export default function DocumentEdit() {
|
|||||||
{/* 修改历史 */}
|
{/* 修改历史 */}
|
||||||
<Card title="修改历史">
|
<Card title="修改历史">
|
||||||
<div className="history-timeline">
|
<div className="history-timeline">
|
||||||
{document.history.map((item: HistoryItem, index: number) => (
|
{[
|
||||||
|
{
|
||||||
|
time: "2023-10-15 15:30",
|
||||||
|
user: "系统",
|
||||||
|
action: "创建了此文档",
|
||||||
|
details: `首次上传文档,文档类型:${getDocumentTypeName(document.type)},状态:待审核`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
time: "2023-10-15 16:45",
|
||||||
|
user: "张三",
|
||||||
|
action: "启动了文档审核",
|
||||||
|
details: "状态由'待审核'变更为'审核中'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
time: "2023-10-15 17:20",
|
||||||
|
user: "系统",
|
||||||
|
action: "完成了文档审核",
|
||||||
|
details: "状态由'审核中'变更为'通过',未发现问题"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
time: "2023-10-16 09:10",
|
||||||
|
user: "李四",
|
||||||
|
action: "修改了文档属性",
|
||||||
|
details: "添加了备注信息,完善了文档编号"
|
||||||
|
}
|
||||||
|
].map((item, index) => (
|
||||||
<div className="timeline-item" key={`${item.time}-${index}`}>
|
<div className="timeline-item" key={`${item.time}-${index}`}>
|
||||||
<div className="timeline-time">{item.time}</div>
|
<div className="timeline-time">{item.time}</div>
|
||||||
<div className="timeline-content">
|
<div className="timeline-content">
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
.document-edit-page .form-textarea,
|
.document-edit-page .form-textarea,
|
||||||
.document-edit-page .form-select {
|
.document-edit-page .form-select {
|
||||||
@apply w-full rounded-md border border-gray-300 shadow-sm px-3 py-2;
|
@apply w-full rounded-md border border-gray-300 shadow-sm px-3 py-2;
|
||||||
@apply focus:outline-none focus:ring-1 focus:ring-primary-500 focus:border-primary-500;
|
@apply focus:outline-none focus:border-[var(--primary-color)] focus:shadow-[0_0_0_2px_rgba(0,104,74,0.2)];
|
||||||
}
|
}
|
||||||
|
|
||||||
.document-edit-page .text-secondary {
|
.document-edit-page .text-secondary {
|
||||||
|
|||||||
Reference in New Issue
Block a user