添加合同和卷宗数据隔离

This commit is contained in:
2025-06-03 12:16:31 +08:00
parent b02978508d
commit 0397139ad8
20 changed files with 1190 additions and 437 deletions
+164 -136
View File
@@ -1,4 +1,4 @@
import { useState, useEffect, useRef } from "react";
import { useState, useEffect, useRef, useCallback } from "react";
import { useSearchParams, useLoaderData, useFetcher, useNavigate,Link } from "@remix-run/react";
import { type MetaFunction, type ActionFunctionArgs, type LoaderFunctionArgs } from "@remix-run/node";
import { Card } from "~/components/ui/Card";
@@ -34,63 +34,28 @@ export const meta: MetaFunction = () => {
// 数据加载器
export const loader = async ({ request }: LoaderFunctionArgs) => {
// 获取URL查询参数
// 获取URL查询参数,只保留必要的分页参数
const url = new URL(request.url);
const search = url.searchParams.get("search") || "";
const documentType = url.searchParams.get("documentType") || "";
const auditStatus = url.searchParams.get("auditStatus") || "";
const documentNumber = url.searchParams.get("documentNumber") || "";
const fileStatus = url.searchParams.get("fileStatus") || "";
const dateFrom = url.searchParams.get("dateFrom") || "";
const dateTo = url.searchParams.get("dateTo") || "";
const page = parseInt(url.searchParams.get("page") || "1", 10);
const pageSize = parseInt(url.searchParams.get("pageSize") || "10", 10);
// 构建搜索参数
const searchParams = {
name: search || undefined,
documentNumber: documentNumber || undefined,
documentType: documentType || undefined,
auditStatus: auditStatus || undefined,
fileStatus: fileStatus || undefined,
dateFrom: dateFrom || undefined,
dateTo: dateTo || undefined,
// 获取文档类型列表,用于筛选条件
const typesResponse = await getDocumentTypes({ pageSize: 500 });
const documentTypes = typesResponse.data?.types || [];
const documentTypeOptions = documentTypes.map(type => ({
value: type.id,
label: type.name
}));
// 初始返回空数据,将在客户端根据 sessionStorage 中的 reviewType 加载实际数据
return Response.json({
documents: [],
total: 0,
page,
pageSize
};
try {
// 获取文档列表
const documentsResponse = await getDocuments(searchParams);
// console.log('documentsResponse---1--',JSON.stringify(documentsResponse,null,2));
if (documentsResponse.error) {
throw new Error(documentsResponse.error);
}
// 获取文档类型列表,用于筛选条件,设置较大的pageSize确保获取所有数据
const typesResponse = await getDocumentTypes({ pageSize: 500 });
// console.log('typesResponse-----',typesResponse);
const documentTypes = typesResponse.data?.types || [];
const documentTypeOptions = documentTypes.map(type => ({
value: type.id,
label: type.name
}));
// console.log('typesResponse-----',JSON.stringify(documentsResponse.data?.documents[1],null,2));
return Response.json({
documents: documentsResponse.data?.documents || [],
total: documentsResponse.data?.total || 0,
page,
pageSize,
documentTypeOptions
});
} catch (error) {
console.error('获取文档列表失败:', error);
return Response.json({
error: '获取文档列表失败',
status: 500
}, { status: 500 });
}
pageSize,
documentTypeOptions,
initialLoad: true // 标记这是初始加载
});
};
// 定义action返回的数据类型
@@ -199,8 +164,14 @@ export default function DocumentsIndex() {
const fetcher = useFetcher<ActionResponse>();
const navigate = useNavigate();
// 存储从 sessionStorage 获取的 reviewType
const [reviewType, setReviewType] = useState<string | null>(null);
// 添加页面加载状态管理
const [isLoadingData, setIsLoadingData] = useState(true);
const [documents, setDocuments] = useState<DocumentUI[]>([]);
const [total, setTotal] = useState(0);
const [filteredDocumentTypeOptions, setFilteredDocumentTypeOptions] = useState(loaderData.documentTypeOptions);
const dataCache = useRef<typeof loaderData | null>(null);
// 从URL获取当前筛选条件
@@ -214,8 +185,80 @@ export default function DocumentsIndex() {
const currentPage = parseInt(searchParams.get("page") || "1", 10);
const pageSize = parseInt(searchParams.get("pageSize") || "10", 10);
// 获取API返回的数据
const { documents, total, documentTypeOptions } = loaderData;
// 客户端数据请求
const fetchData = useCallback(async (storedReviewType: string) => {
setIsLoadingData(true);
loadingBarService.show();
try {
// 构建搜索参数
const searchParams = {
name: search || undefined,
documentNumber: documentNumber || undefined,
documentType: documentType || undefined,
auditStatus: auditStatus || undefined,
fileStatus: fileStatus || undefined,
dateFrom: dateFrom || undefined,
dateTo: dateTo || undefined,
reviewType: storedReviewType || undefined,
page: currentPage,
pageSize
};
// 获取文档列表
const documentsResponse = await getDocuments(searchParams);
if (documentsResponse.error) {
throw new Error(documentsResponse.error);
}
// 获取经过过滤的文档类型列表
const filteredTypesResponse = await getDocumentTypes({
pageSize: 500,
reviewType: storedReviewType || undefined
});
const filteredDocumentTypes = filteredTypesResponse.data?.types || [];
const filteredOptions = filteredDocumentTypes.map(type => ({
value: type.id,
label: type.name
}));
// 更新状态
setDocuments(documentsResponse.data?.documents || []);
setTotal(documentsResponse.data?.total || 0);
setFilteredDocumentTypeOptions(filteredOptions);
} catch (error) {
console.error('获取文档列表失败:', error);
toastService.error('获取文档列表失败: ' + (error instanceof Error ? error.message : '未知错误'));
} finally {
setIsLoadingData(false);
loadingBarService.hide();
}
}, [search, documentNumber, documentType, auditStatus, fileStatus, dateFrom, dateTo, currentPage, pageSize]);
// 在组件挂载时从 sessionStorage 获取 reviewType 并加载数据
useEffect(() => {
try {
if (typeof window !== 'undefined') {
const storedReviewType = sessionStorage.getItem('reviewType');
setReviewType(storedReviewType);
// 如果有 reviewType,则加载数据
if (storedReviewType) {
fetchData(storedReviewType);
}
}
} catch (error) {
console.error('获取 sessionStorage 中的 reviewType 失败:', error);
}
}, [fetchData]);
// 监听 URL 参数变化,重新获取数据
useEffect(() => {
if (reviewType) {
fetchData(reviewType);
}
}, [searchParams, fetchData, reviewType]);
// 使用并更新缓存数据
useEffect(() => {
@@ -231,10 +274,6 @@ export default function DocumentsIndex() {
// 设置缓存数据
dataCache.current = loaderData;
// 数据加载完成后,执行额外的延迟以确保UI效果
setIsLoadingData(false);
loadingBarService.hide();
// 处理loader错误
if (loaderData.error) {
toastService.error(loaderData.error);
@@ -808,84 +847,73 @@ export default function DocumentsIndex() {
</div>
{/* 搜索筛选区 */}
<FilterPanel
actions={
<>
<Button
type="default"
icon="ri-refresh-line"
onClick={handleReset}
className="mr-2"
>
</Button>
{/* <Button
type="primary"
icon="ri-search-line"
onClick={() => {
// 保持当前筛选条件,刷新数据
// 在实际应用中,这里可能需要触发某些操作
}}
>
搜索
</Button> */}
</>
}
noActionDivider={true}
>
<div className="grid grid-cols-2 md:grid-cols-3 gap-4 w-full">
<SearchFilter
label="文档名称"
placeholder="请输入文档名称"
value={search}
onSearch={handleNameSearch}
instantSearch={true}
/>
<FilterPanel
actions={
<>
<Button
type="default"
icon="ri-refresh-line"
onClick={handleReset}
className="mr-2"
>
</Button>
</>
}
noActionDivider={true}
>
<div className="grid grid-cols-2 md:grid-cols-3 gap-4 w-full">
<SearchFilter
label="文档名称"
placeholder="请输入文档名称"
value={search}
onSearch={handleNameSearch}
instantSearch={true}
/>
<SearchFilter
label="文档编号"
placeholder="请输入文档编号"
value={documentNumber}
onSearch={handleDocumentNumberChange}
instantSearch={true}
/>
<FilterSelect
label="文档类型"
name="documentType"
value={documentType}
options={documentTypeOptions}
onChange={handleDocumentTypeChange}
/>
<FilterSelect
label="文件状态"
name="fileStatus"
value={fileStatus}
options={fileStatusOptions}
onChange={handleFileStatusChange}
/>
<FilterSelect
label="审核状态"
name="auditStatus"
value={auditStatus}
options={auditStatusOptions}
onChange={handleStatusChange}
/>
<DateRangeFilter
label="上传时间"
startDate={dateFrom}
endDate={dateTo}
onStartDateChange={(value) => handleDateChange('dateFrom', value)}
onEndDateChange={(value) => handleDateChange('dateTo', value)}
simple={true}
colorMode="light"
/>
</div>
</FilterPanel>
<SearchFilter
label="文档编号"
placeholder="请输入文档编号"
value={documentNumber}
onSearch={handleDocumentNumberChange}
instantSearch={true}
/>
<FilterSelect
label="文档类型"
name="documentType"
value={documentType}
options={filteredDocumentTypeOptions}
onChange={handleDocumentTypeChange}
/>
<FilterSelect
label="文件状态"
name="fileStatus"
value={fileStatus}
options={fileStatusOptions}
onChange={handleFileStatusChange}
/>
<FilterSelect
label="审核状态"
name="auditStatus"
value={auditStatus}
options={auditStatusOptions}
onChange={handleStatusChange}
/>
<DateRangeFilter
label="上传时间"
startDate={dateFrom}
endDate={dateTo}
onStartDateChange={(value) => handleDateChange('dateFrom', value)}
onEndDateChange={(value) => handleDateChange('dateTo', value)}
simple={true}
colorMode="light"
/>
</div>
</FilterPanel>
{/* 数据表格 */}
<Card>