Files
leaudit-platform-frontend/app/routes/contract-template.search._index.tsx
T

120 lines
3.7 KiB
TypeScript

import type { MetaFunction, LoaderFunctionArgs } from '@remix-run/node';
import { useNavigate, useLoaderData } from '@remix-run/react';
import { ContractSearchHero } from '~/components/contract-template/ContractSearchHero';
import { getContractCategoriesWithCount } from '~/api/contract-template/templates';
import type { ContractCategoryWithCount } from '~/api/contract-template/templates';
import styles from '~/styles/pages/contract-template.css?url';
import { getUserSession } from '~/api/login/auth.server';
export const links = () => [
{ rel: 'stylesheet', href: styles }
];
export const meta: MetaFunction = () => {
return [
{ title: 'AI智能合同模板搜索 - 智慧法务' },
{
name: 'description',
content: '使用AI智能搜索快速找到最适合的合同模板,支持自然语言描述搜索。'
}
];
};
// 面包屑导航配置
export const handle = {
breadcrumb: "智能搜索"
};
/**
* 转换分类数据为前端显示格式
* @param category 分类数据
* @returns 转换后的分类数据
*/
function transformCategory(category: ContractCategoryWithCount) {
return {
id: category.id,
name: category.name,
icon: category.icon || 'ri-file-text-line',
count: category.template_count
};
}
/**
* 加载分类数据
* @returns 分类数据
*/
export async function loader({ request }: LoaderFunctionArgs) {
const url = new URL(request.url);
const { handleServerAuth } = await import("~/utils/server-auth-handler");
return handleServerAuth(async () => {
// 获取 JWT
const { frontendJWT } = await getUserSession(request);
const jwt = frontendJWT || undefined;
// 使用聚合查询获取分类及其模板数量
const categoriesResponse = await getContractCategoriesWithCount(jwt);
// 处理分类数据
if (categoriesResponse.error) {
console.error('获取分类失败:', categoriesResponse.error);
return { categories: [] };
}
const categories = categoriesResponse.data || [];
// 转换分类数据格式
const categoriesWithCount = categories.map(transformCategory);
return { categories: categoriesWithCount };
}, url.pathname);
}
export default function ContractTemplateSearchIndex() {
const navigate = useNavigate();
const { categories } = useLoaderData<typeof loader>();
const handleSearch = (query: string) => {
if (query.trim()) {
navigate(`/contract-template/search/results?q=${encodeURIComponent(query)}`);
}
};
/**
* 处理分类点击事件 - 使用ID而不是名称
* @param categoryId 分类ID
*/
const handleCategoryClick = (categoryId: number) => {
navigate(`/contract-template/list?category_id=${categoryId}`);
};
return (
<div className="contract-template-search">
<ContractSearchHero onSearch={handleSearch} />
<div className="quick-categories">
{categories.map((category, index) => (
<div
key={category.id || index}
className="category-card"
onClick={() => handleCategoryClick(category.id)}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleCategoryClick(category.id);
}
}}
role="button"
tabIndex={0}
aria-label={`选择${category.name}分类,共${category.count}个模板`}
>
<div className="category-icon">
<i className={category.icon}></i>
</div>
<div className="category-title">{category.name}</div>
<div className="category-count">{category.count}</div>
</div>
))}
</div>
</div>
);
}