合同基本列表数据查询基本完善
This commit is contained in:
@@ -6,6 +6,8 @@ import { SearchResultHeader } from '~/components/contract-template/SearchResultH
|
||||
import { FilterTabs } from '~/components/contract-template/FilterTabs';
|
||||
import { TemplateGrid } from '~/components/contract-template/TemplateGrid';
|
||||
import { Pagination } from '~/components/ui/Pagination';
|
||||
import { searchContractTemplates, getContractCategories } from '~/api/contract-template/templates';
|
||||
import type { ContractTemplate, ContractCategory } from '~/api/contract-template/templates';
|
||||
import styles from '~/styles/pages/contract-template.css?url';
|
||||
|
||||
export const links = () => [
|
||||
@@ -27,172 +29,161 @@ export const handle = {
|
||||
breadcrumb: "搜索结果"
|
||||
};
|
||||
|
||||
// 模拟数据 - 扩展搜索结果
|
||||
const mockSearchResults = [
|
||||
{
|
||||
id: '1',
|
||||
title: '烟草产品销售合同标准模板',
|
||||
type: '销售合同',
|
||||
description: '适用于烟草产品销售业务,包含完整的违约责任条款、付款方式、交付条件等核心要素,符合行业规范要求。',
|
||||
updateTime: '2023-10-25',
|
||||
useCount: 1248,
|
||||
rating: 4.8,
|
||||
category: '销售合同'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: '零售商销售协议模板',
|
||||
type: '销售合同',
|
||||
description: '专为零售商设计的销售协议,详细规定了违约责任、退换货政策、结算方式等条款。',
|
||||
updateTime: '2023-10-20',
|
||||
useCount: 856,
|
||||
rating: 4.6,
|
||||
category: '销售合同'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
title: '设备采购合同(含违约条款)',
|
||||
type: '采购合同',
|
||||
description: '设备采购专用合同模板,包含详细的违约责任条款、质量保证、验收标准等内容。',
|
||||
updateTime: '2023-10-18',
|
||||
useCount: 642,
|
||||
rating: 4.7,
|
||||
category: '采购合同'
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
title: '批发销售合同模板',
|
||||
type: '销售合同',
|
||||
description: '适用于大宗批发业务的销售合同,强化了违约责任条款和风险控制措施。',
|
||||
updateTime: '2023-10-15',
|
||||
useCount: 534,
|
||||
rating: 4.5,
|
||||
category: '销售合同'
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
title: '技术服务合同(标准版)',
|
||||
type: '服务合同',
|
||||
description: '技术服务类合同模板,包含服务标准、违约责任、知识产权保护等关键条款。',
|
||||
updateTime: '2023-10-12',
|
||||
useCount: 423,
|
||||
rating: 4.4,
|
||||
category: '服务合同'
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
title: '区域代理销售合同',
|
||||
type: '销售合同',
|
||||
description: '区域代理商专用销售合同,明确代理权限、销售目标、违约责任等核心条款。',
|
||||
updateTime: '2023-10-10',
|
||||
useCount: 312,
|
||||
rating: 4.3,
|
||||
category: '销售合同'
|
||||
},
|
||||
{
|
||||
id: '7',
|
||||
title: '物流运输服务合同',
|
||||
type: '物流运输',
|
||||
description: '专业的物流运输服务合同模板,涵盖运输责任、保险、违约赔偿等关键条款。',
|
||||
updateTime: '2023-10-08',
|
||||
useCount: 267,
|
||||
rating: 4.2,
|
||||
category: '物流运输'
|
||||
},
|
||||
{
|
||||
id: '8',
|
||||
title: '仓储管理服务协议',
|
||||
type: '物流运输',
|
||||
description: '仓储管理专用合同,包含货物保管、出入库管理、损失责任等详细条款。',
|
||||
updateTime: '2023-10-05',
|
||||
useCount: 189,
|
||||
rating: 4.1,
|
||||
category: '物流运输'
|
||||
},
|
||||
{
|
||||
id: '9',
|
||||
title: '劳务派遣合同模板',
|
||||
type: '人事劳务',
|
||||
description: '劳务派遣服务合同,明确派遣关系、工资福利、社保缴纳等人事管理条款。',
|
||||
updateTime: '2023-10-03',
|
||||
useCount: 345,
|
||||
rating: 4.6,
|
||||
category: '人事劳务'
|
||||
},
|
||||
{
|
||||
id: '10',
|
||||
title: '商业机密保护协议',
|
||||
type: '保密协议',
|
||||
description: '企业商业机密保护专用协议,涵盖信息范围、保密义务、违约责任等核心内容。',
|
||||
updateTime: '2023-10-01',
|
||||
useCount: 156,
|
||||
rating: 4.5,
|
||||
category: '保密协议'
|
||||
},
|
||||
{
|
||||
id: '11',
|
||||
title: '办公场地租赁合同',
|
||||
type: '租赁合同',
|
||||
description: '办公场地租赁标准合同,包含租金支付、物业管理、违约处理等全面条款。',
|
||||
updateTime: '2023-09-28',
|
||||
useCount: 278,
|
||||
rating: 4.3,
|
||||
category: '租赁合同'
|
||||
},
|
||||
{
|
||||
id: '12',
|
||||
title: '设备租赁协议(长期)',
|
||||
type: '租赁合同',
|
||||
description: '长期设备租赁专用协议,详细规定租赁期限、维护责任、续租条件等关键条款。',
|
||||
updateTime: '2023-09-25',
|
||||
useCount: 198,
|
||||
rating: 4.4,
|
||||
category: '租赁合同'
|
||||
}
|
||||
];
|
||||
// 带搜索统计的分类类型
|
||||
interface CategoryWithSearchCount extends ContractCategory {
|
||||
searchCount: number;
|
||||
}
|
||||
|
||||
// 前端显示的模板类型
|
||||
interface DisplayTemplate {
|
||||
id: string;
|
||||
title: string;
|
||||
type: string;
|
||||
description: string;
|
||||
updateTime: string;
|
||||
useCount: number;
|
||||
rating: number;
|
||||
category: string;
|
||||
}
|
||||
|
||||
// 将数据库模板转换为前端显示格式
|
||||
function transformTemplate(template: ContractTemplate) {
|
||||
// 模拟使用次数和评分(实际项目中可以从其他表获取)
|
||||
const mockUsageCount = Math.floor(Math.random() * 2000) + 100;
|
||||
const mockRating = (Math.random() * 1.5 + 3.5).toFixed(1);
|
||||
|
||||
return {
|
||||
id: template.id.toString(),
|
||||
title: template.title,
|
||||
type: template.is_featured ? '推荐版' : '标准版',
|
||||
description: template.description || '',
|
||||
updateTime: new Date(template.updated_at).toLocaleDateString('zh-CN'),
|
||||
useCount: mockUsageCount,
|
||||
rating: parseFloat(mockRating),
|
||||
category: template.category?.name || '其他'
|
||||
};
|
||||
}
|
||||
|
||||
export async function loader({ request }: LoaderFunctionArgs) {
|
||||
const url = new URL(request.url);
|
||||
const query = url.searchParams.get('q') || '';
|
||||
const category = url.searchParams.get('category') || '';
|
||||
const page = parseInt(url.searchParams.get('page') || '1');
|
||||
const pageSize = 6;
|
||||
|
||||
// 模拟搜索耗时
|
||||
// 记录搜索开始时间
|
||||
const startTime = Date.now();
|
||||
|
||||
// 这里应该是实际的搜索逻辑
|
||||
// 目前返回模拟数据
|
||||
let filteredResults = mockSearchResults;
|
||||
|
||||
// 如果有查询条件,进行筛选
|
||||
if (query || category) {
|
||||
filteredResults = mockSearchResults.filter(item => {
|
||||
const matchesQuery = !query ||
|
||||
item.title.toLowerCase().includes(query.toLowerCase()) ||
|
||||
item.description.toLowerCase().includes(query.toLowerCase());
|
||||
const matchesCategory = !category || item.category === category;
|
||||
return matchesQuery && matchesCategory;
|
||||
});
|
||||
try {
|
||||
// 并行获取搜索结果和分类数据
|
||||
const [searchResponse, categoriesResponse] = await Promise.all([
|
||||
searchContractTemplates(query, {
|
||||
category,
|
||||
page,
|
||||
pageSize,
|
||||
sortBy: 'updated_at',
|
||||
sortOrder: 'desc'
|
||||
}),
|
||||
getContractCategories()
|
||||
]);
|
||||
|
||||
// 处理搜索结果
|
||||
if (searchResponse.error) {
|
||||
console.error('搜索合同模板失败:', searchResponse.error);
|
||||
return {
|
||||
results: [],
|
||||
query,
|
||||
category,
|
||||
total: 0,
|
||||
page,
|
||||
pageSize,
|
||||
searchTime: '搜索失败',
|
||||
categories: []
|
||||
};
|
||||
}
|
||||
|
||||
// 处理分类数据
|
||||
const categories = categoriesResponse.error ? [] : categoriesResponse.data || [];
|
||||
|
||||
// 转换模板数据格式
|
||||
const transformedResults = searchResponse.data?.templates.map(transformTemplate) || [];
|
||||
|
||||
// 为每个分类获取搜索结果统计
|
||||
let categoriesWithSearchCount: CategoryWithSearchCount[] = [];
|
||||
if (query && query.trim()) {
|
||||
// 并行为每个分类获取搜索结果数量
|
||||
const categorySearchPromises = categories.map(async (cat): Promise<CategoryWithSearchCount> => {
|
||||
try {
|
||||
const categorySearchResponse = await searchContractTemplates(query, {
|
||||
category: cat.name,
|
||||
page: 1,
|
||||
pageSize: 1000 // 设置较大的pageSize来获取总数
|
||||
});
|
||||
|
||||
const count = categorySearchResponse.error ? 0 : (categorySearchResponse.data?.total || 0);
|
||||
return {
|
||||
...cat,
|
||||
searchCount: count
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(`获取分类${cat.name}的搜索统计失败:`, error);
|
||||
return {
|
||||
...cat,
|
||||
searchCount: 0
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
categoriesWithSearchCount = await Promise.all(categorySearchPromises);
|
||||
} else {
|
||||
// 如果没有搜索关键词,searchCount设为0
|
||||
categoriesWithSearchCount = categories.map(cat => ({
|
||||
...cat,
|
||||
searchCount: 0
|
||||
}));
|
||||
}
|
||||
|
||||
// 计算搜索耗时
|
||||
const endTime = Date.now();
|
||||
const searchTime = (endTime - startTime) / 1000;
|
||||
const searchTimeText = `搜索用时 ${searchTime.toFixed(1)}秒`;
|
||||
|
||||
return {
|
||||
results: transformedResults,
|
||||
query,
|
||||
category,
|
||||
total: searchResponse.data?.total || 0,
|
||||
page,
|
||||
pageSize,
|
||||
searchTime: searchTimeText,
|
||||
categories: categoriesWithSearchCount
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('加载搜索结果失败:', error);
|
||||
return {
|
||||
results: [],
|
||||
query,
|
||||
category,
|
||||
total: 0,
|
||||
page,
|
||||
pageSize,
|
||||
searchTime: '搜索失败',
|
||||
categories: []
|
||||
};
|
||||
}
|
||||
|
||||
// 计算搜索耗时
|
||||
const endTime = Date.now();
|
||||
const searchTime = (endTime - startTime) / 1000;
|
||||
const searchTimeText = `搜索用时 ${searchTime.toFixed(1)}秒`;
|
||||
|
||||
return {
|
||||
results: filteredResults,
|
||||
query,
|
||||
category,
|
||||
total: filteredResults.length,
|
||||
page,
|
||||
pageSize: 6,
|
||||
searchTime: searchTimeText
|
||||
};
|
||||
}
|
||||
|
||||
export default function ContractTemplateSearchResults() {
|
||||
const { results, query, total, page, pageSize, searchTime } = useLoaderData<typeof loader>();
|
||||
const { results, query, total, page, pageSize, searchTime, categories }: {
|
||||
results: DisplayTemplate[];
|
||||
query: string;
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
searchTime: string;
|
||||
categories: CategoryWithSearchCount[];
|
||||
} = useLoaderData<typeof loader>();
|
||||
|
||||
const [searchParams] = useSearchParams();
|
||||
const navigate = useNavigate();
|
||||
const [activeFilter, setActiveFilter] = useState('全部');
|
||||
@@ -211,7 +202,16 @@ export default function ContractTemplateSearchResults() {
|
||||
|
||||
const handleFilterChange = (filter: string) => {
|
||||
setActiveFilter(filter);
|
||||
// 这里可以添加实际的筛选逻辑
|
||||
const params = new URLSearchParams(searchParams);
|
||||
|
||||
if (filter === '全部') {
|
||||
params.delete('category');
|
||||
} else {
|
||||
params.set('category', filter);
|
||||
}
|
||||
params.delete('page'); // 重置页码
|
||||
|
||||
navigate(`/contract-template/search/results?${params.toString()}`);
|
||||
};
|
||||
|
||||
const handlePageChange = (newPage: number) => {
|
||||
@@ -220,11 +220,13 @@ export default function ContractTemplateSearchResults() {
|
||||
navigate(`/contract-template/search/results?${params.toString()}`);
|
||||
};
|
||||
|
||||
// 动态生成筛选选项
|
||||
const filters = [
|
||||
{ label: '全部', count: total },
|
||||
{ label: '销售合同', count: results.filter(r => r.category === '销售合同').length },
|
||||
{ label: '采购合同', count: results.filter(r => r.category === '采购合同').length },
|
||||
{ label: '服务合同', count: results.filter(r => r.category === '服务合同').length }
|
||||
...categories.map(cat => ({
|
||||
label: cat.name,
|
||||
count: cat.searchCount || 0
|
||||
}))
|
||||
];
|
||||
|
||||
const totalPages = Math.ceil(total / pageSize);
|
||||
|
||||
Reference in New Issue
Block a user