274 lines
7.6 KiB
TypeScript
274 lines
7.6 KiB
TypeScript
|
|
'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>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|