274 lines
7.6 KiB
TypeScript
Executable File
274 lines
7.6 KiB
TypeScript
Executable File
'use client';
|
||
|
||
import { ProfileDataTable } from './profile-data-table';
|
||
import { useProfile } from './profile-provider';
|
||
import { useTRPC } from '@fenghuo/client';
|
||
import { useState, useEffect, useMemo } from 'react';
|
||
import { useQuery } from '@tanstack/react-query';
|
||
|
||
interface PaginationInfo {
|
||
page: number;
|
||
pageSize: number;
|
||
totalPages: number;
|
||
totalCount: number;
|
||
hasNextPage: boolean;
|
||
hasPreviousPage: boolean;
|
||
}
|
||
|
||
export function ProfileOverview() {
|
||
const {
|
||
handleAddEmployee,
|
||
handleViewDetail,
|
||
handleDeleteEmployee,
|
||
registerRefetch,
|
||
} = useProfile();
|
||
|
||
const [currentPage, setCurrentPage] = useState(1);
|
||
const [pageSize, setPageSize] = useState(10);
|
||
const [searchValue, setSearchValue] = useState('');
|
||
const [debouncedSearchValue, setDebouncedSearchValue] = useState('');
|
||
const [selectedOrganizationIds, setSelectedOrganizationIds] = useState<string[]>([]);
|
||
const [sortBy, setSortBy] = useState<string>('default');
|
||
|
||
// 使用 useEffect 实现防抖
|
||
useEffect(() => {
|
||
const timer = setTimeout(() => {
|
||
setDebouncedSearchValue(searchValue);
|
||
}, 300);
|
||
|
||
return () => clearTimeout(timer);
|
||
}, [searchValue]);
|
||
|
||
const trpc = useTRPC();
|
||
|
||
// 修改搜索条件构建逻辑
|
||
const searchConditions = useMemo(() => {
|
||
const conditions: any = {};
|
||
|
||
// 部门筛选条件 - 使用组织路径查询包含子组织
|
||
if (selectedOrganizationIds.length > 0) {
|
||
const organizationConditions = selectedOrganizationIds.flatMap(organizationId => [
|
||
// 直接匹配当前组织
|
||
{ organizationId: organizationId },
|
||
// 匹配所有子组织(通过路径包含当前组织ID)
|
||
{
|
||
organization: {
|
||
path: {
|
||
contains: organizationId
|
||
}
|
||
}
|
||
}
|
||
]);
|
||
|
||
conditions.OR = organizationConditions;
|
||
}
|
||
|
||
// 搜索条件
|
||
if (debouncedSearchValue.trim()) {
|
||
const searchTerm = debouncedSearchValue.trim();
|
||
const baseConditions: any[] = [
|
||
{ name: { contains: searchTerm, mode: 'insensitive' as const } },
|
||
// 身份证号搜索
|
||
{ idNum: { contains: searchTerm, mode: 'insensitive' as const } },
|
||
// 证件号搜索
|
||
{ paperId: { contains: searchTerm, mode: 'insensitive' as const } },
|
||
// 身份搜索
|
||
{ identity: { contains: searchTerm, mode: 'insensitive' as const } },
|
||
// 等级搜索
|
||
{ level: { contains: searchTerm, mode: 'insensitive' as const } },
|
||
// 职务名称搜索
|
||
{ dutyName: { contains: searchTerm, mode: 'insensitive' as const } },
|
||
// 职务代码搜索
|
||
{ dutyCode: { contains: searchTerm, mode: 'insensitive' as const } },
|
||
// 组织名称搜索
|
||
{ organization: { name: { contains: searchTerm, mode: 'insensitive' as const } } },
|
||
// 代理职务
|
||
{ metadata: { path: ['proxyDuty'], string_contains: searchTerm } },
|
||
// 政治面貌搜索
|
||
{ metadata: { path: ['politicalStatus'], string_contains: searchTerm } },
|
||
// 党派职务搜索
|
||
{ metadata: { path: ['partyPosition'], string_contains: searchTerm } },
|
||
// 学历搜索
|
||
{ metadata: { path: ['education'], string_contains: searchTerm } },
|
||
// 学历形式搜索
|
||
{ metadata: { path: ['educationForm'], string_contains: searchTerm } },
|
||
// 学校专业搜索
|
||
{ metadata: { path: ['schoolMajor'], string_contains: searchTerm } },
|
||
// 籍贯搜索
|
||
{ metadata: { path: ['native', 'province'], string_contains: searchTerm } },
|
||
// 籍贯城市搜索
|
||
{ metadata: { path: ['native', 'city'], string_contains: searchTerm } },
|
||
// 籍贯县区搜索
|
||
{ metadata: { path: ['native', 'county'], string_contains: searchTerm } },
|
||
];
|
||
|
||
// 性别搜索:支持"男"、"女"关键词
|
||
if (searchTerm === '男' || searchTerm.toLowerCase() === 'male') {
|
||
baseConditions.push({
|
||
gender: {
|
||
equals: 1,
|
||
},
|
||
});
|
||
} else if (searchTerm === '女' || searchTerm.toLowerCase() === 'female') {
|
||
baseConditions.push({
|
||
gender: {
|
||
not: 1,
|
||
},
|
||
});
|
||
}
|
||
|
||
// 如果同时有部门筛选和搜索条件,需要合并条件
|
||
if (selectedOrganizationIds.length > 0) {
|
||
const organizationConditions = selectedOrganizationIds.flatMap(organizationId => [
|
||
// 直接匹配当前组织
|
||
{ organizationId: organizationId },
|
||
// 匹配所有子组织(通过路径包含当前组织ID)
|
||
{
|
||
organization: {
|
||
path: {
|
||
contains: organizationId
|
||
}
|
||
}
|
||
}
|
||
]);
|
||
|
||
conditions.AND = [
|
||
{
|
||
OR: organizationConditions
|
||
},
|
||
{
|
||
OR: baseConditions
|
||
}
|
||
];
|
||
} else {
|
||
conditions.OR = baseConditions;
|
||
}
|
||
}
|
||
|
||
return conditions;
|
||
}, [debouncedSearchValue, selectedOrganizationIds]);
|
||
|
||
// 构建排序条件
|
||
const orderByCondition = useMemo(() => {
|
||
switch (sortBy) {
|
||
case 'dutyLevelDown':
|
||
return { dutyLevel: 'desc' as const };
|
||
case 'dutyLevelUp':
|
||
return { dutyLevel: 'asc' as const };
|
||
case 'hireDateDown':
|
||
return { hireDate: 'desc' as const };
|
||
case 'hireDateUp':
|
||
return { hireDate: 'asc' as const };
|
||
case 'birthdayDown':
|
||
return { birthday: 'desc' as const };
|
||
case 'birthdayUp':
|
||
return { birthday: 'asc' as const };
|
||
default:
|
||
return { createdAt: 'desc' as const };
|
||
}
|
||
}, [sortBy]);
|
||
|
||
const { data: profileResponse, isLoading: profilesLoading, refetch } = useQuery({
|
||
...trpc.profile.findManyWithPagination.queryOptions({
|
||
where: {
|
||
deletedAt: null,
|
||
...searchConditions,
|
||
},
|
||
page: currentPage,
|
||
pageSize: pageSize,
|
||
orderBy: orderByCondition,
|
||
include: {
|
||
organization: {
|
||
include: {
|
||
children: true,
|
||
parent: true,
|
||
terms: {
|
||
include: {
|
||
taxonomy: true,
|
||
}
|
||
}
|
||
},
|
||
},
|
||
},
|
||
}),
|
||
});
|
||
|
||
// 注册refetch函数到provider
|
||
useEffect(() => {
|
||
registerRefetch(refetch);
|
||
}, [registerRefetch, refetch]);
|
||
|
||
// 搜索时重置到第一页
|
||
useEffect(() => {
|
||
if (debouncedSearchValue !== searchValue) {
|
||
setCurrentPage(1);
|
||
}
|
||
}, [debouncedSearchValue, searchValue]);
|
||
|
||
// 部门筛选时重置到第一页
|
||
useEffect(() => {
|
||
if (selectedOrganizationIds.length > 0) {
|
||
setCurrentPage(1);
|
||
}
|
||
}, [selectedOrganizationIds]);
|
||
const handlePageChange = (page: number) => {
|
||
setCurrentPage(page);
|
||
};
|
||
|
||
const handlePageSizeChange = (size: number) => {
|
||
setPageSize(size);
|
||
setCurrentPage(1);
|
||
};
|
||
|
||
// 处理部门筛选变化
|
||
const handleOrganizationChange = (organizationIds: string[]) => {
|
||
setSelectedOrganizationIds(organizationIds);
|
||
setCurrentPage(1); // 筛选时重置到第一页
|
||
};
|
||
|
||
// 修改现有的搜索变化处理
|
||
const handleSearchChange = (value: string) => {
|
||
setSearchValue(value);
|
||
setCurrentPage(1); // 搜索时重置到第一页
|
||
};
|
||
|
||
// 处理排序变化
|
||
const handleSortChange = (value: string) => {
|
||
setSortBy(value);
|
||
setCurrentPage(1); // 排序时重置到第一页
|
||
};
|
||
|
||
const pagination: PaginationInfo = {
|
||
page: currentPage,
|
||
pageSize: pageSize,
|
||
totalPages: profileResponse?.totalPages || 0,
|
||
totalCount: profileResponse?.totalCount || 0,
|
||
hasNextPage: profileResponse?.hasNextPage || false,
|
||
hasPreviousPage: profileResponse?.hasPreviousPage || false,
|
||
};
|
||
|
||
return (
|
||
<div className="w-full">
|
||
<ProfileDataTable
|
||
data={profileResponse?.items || []}
|
||
pagination={pagination}
|
||
onPageChange={handlePageChange}
|
||
onPageSizeChange={handlePageSizeChange}
|
||
onAddEmployee={handleAddEmployee}
|
||
onViewDetail={handleViewDetail}
|
||
onDeleteEmployee={(profileId: string) => handleDeleteEmployee(profileId, refetch)}
|
||
isLoading={profilesLoading}
|
||
searchValue={searchValue}
|
||
onSearchChange={handleSearchChange}
|
||
// 添加部门筛选相关属性
|
||
selectedOrganizationIds={selectedOrganizationIds}
|
||
onOrganizationChange={handleOrganizationChange}
|
||
// 添加排序相关属性
|
||
sortBy={sortBy}
|
||
onSortChange={handleSortChange}
|
||
/>
|
||
</div>
|
||
);
|
||
}
|
||
|