|
|
@@ -31,12 +31,22 @@ import type {
|
|
|
AreaSelection,
|
|
|
} from '../api/types';
|
|
|
|
|
|
+/**
|
|
|
+ * 身份证号脱敏处理
|
|
|
+ * 只显示前6位和后4位,中间用****代替
|
|
|
+ * @param idCard 身份证号
|
|
|
+ * @returns 脱敏后的身份证号
|
|
|
+ */
|
|
|
+const maskIdCard = (idCard: string): string => {
|
|
|
+ if (!idCard || idCard.length < 8) return idCard;
|
|
|
+ return `${idCard.slice(0, 6)}****${idCard.slice(-4)}`;
|
|
|
+};
|
|
|
+
|
|
|
interface DisabledPersonSelectorProps {
|
|
|
open: boolean;
|
|
|
onOpenChange: (open: boolean) => void;
|
|
|
onSelect: (person: DisabledPersonData | DisabledPersonData[]) => void;
|
|
|
mode?: 'single' | 'multiple';
|
|
|
- selectedIds?: number[];
|
|
|
disabledIds?: number[];
|
|
|
}
|
|
|
|
|
|
@@ -47,18 +57,31 @@ const DisabledPersonSelector: React.FC<DisabledPersonSelectorProps> = ({
|
|
|
mode = 'single',
|
|
|
disabledIds = [],
|
|
|
}) => {
|
|
|
+ // 多字段搜索状态管理
|
|
|
const [searchParams, setSearchParams] = useState<{
|
|
|
- keyword: string;
|
|
|
+ name: string;
|
|
|
+ gender: string;
|
|
|
+ disabilityId: string;
|
|
|
+ phone: string;
|
|
|
+ disabilityType: string;
|
|
|
+ disabilityLevel: string;
|
|
|
page: number;
|
|
|
pageSize: number;
|
|
|
}>({
|
|
|
- keyword: '',
|
|
|
+ name: '',
|
|
|
+ gender: '',
|
|
|
+ disabilityId: '',
|
|
|
+ phone: '',
|
|
|
+ disabilityType: '',
|
|
|
+ disabilityLevel: '',
|
|
|
page: 1,
|
|
|
pageSize: 10,
|
|
|
});
|
|
|
const [selectedPersons, setSelectedPersons] = useState<DisabledPersonData[]>([]);
|
|
|
|
|
|
const [areaSelection, setAreaSelection] = useState<AreaSelection>({});
|
|
|
+ // 用于搜索的地区名称
|
|
|
+ const [searchAreaNames, setSearchAreaNames] = useState<{ province?: string; city?: string; district?: string }>({});
|
|
|
const [showBlacklistConfirm, setShowBlacklistConfirm] = useState(false);
|
|
|
const [pendingSelection, setPendingSelection] = useState<DisabledPersonData | DisabledPersonData[] | null>(null);
|
|
|
|
|
|
@@ -66,12 +89,27 @@ const DisabledPersonSelector: React.FC<DisabledPersonSelectorProps> = ({
|
|
|
const form = useForm();
|
|
|
|
|
|
// 搜索残疾人列表
|
|
|
- const { data, isLoading, refetch } = useQuery({
|
|
|
- queryKey: ['disabled-persons-search', searchParams],
|
|
|
+ const { data, isLoading } = useQuery({
|
|
|
+ queryKey: ['disabled-persons-search', searchParams, searchAreaNames],
|
|
|
queryFn: async () => {
|
|
|
+ // 组合多个搜索字段为keyword
|
|
|
+ const searchFields = [
|
|
|
+ searchParams.name,
|
|
|
+ searchParams.gender,
|
|
|
+ searchParams.disabilityId,
|
|
|
+ searchParams.phone,
|
|
|
+ searchParams.disabilityType,
|
|
|
+ searchParams.disabilityLevel,
|
|
|
+ searchAreaNames.province,
|
|
|
+ searchAreaNames.city,
|
|
|
+ searchAreaNames.district,
|
|
|
+ ].filter(Boolean);
|
|
|
+
|
|
|
+ const keyword = searchFields.join(' ').trim();
|
|
|
+
|
|
|
const response = await disabilityClientManager.get().searchDisabledPersons.$get({
|
|
|
query: {
|
|
|
- keyword: searchParams.keyword || '',
|
|
|
+ keyword: keyword || '',
|
|
|
skip: ((searchParams.page || 1) - 1) * (searchParams.pageSize || 10),
|
|
|
take: searchParams.pageSize || 10,
|
|
|
},
|
|
|
@@ -79,34 +117,28 @@ const DisabledPersonSelector: React.FC<DisabledPersonSelectorProps> = ({
|
|
|
if (response.status !== 200) throw new Error('搜索残疾人失败');
|
|
|
return await response.json();
|
|
|
},
|
|
|
- enabled: open && !!searchParams.keyword,
|
|
|
+ enabled: open,
|
|
|
});
|
|
|
|
|
|
- // 获取所有残疾人列表(当没有搜索关键词时)
|
|
|
- const { data: allData, isLoading: isLoadingAll } = useQuery({
|
|
|
- queryKey: ['disabled-persons-all', searchParams.page, searchParams.pageSize],
|
|
|
- queryFn: async () => {
|
|
|
- const response = await disabilityClientManager.get().getAllDisabledPersons.$get({
|
|
|
- query: {
|
|
|
- skip: ((searchParams.page || 1) - 1) * (searchParams.pageSize || 10),
|
|
|
- take: searchParams.pageSize || 10,
|
|
|
- },
|
|
|
- });
|
|
|
- if (response.status !== 200) throw new Error('获取残疾人列表失败');
|
|
|
- return await response.json();
|
|
|
- },
|
|
|
- enabled: open && !searchParams.keyword,
|
|
|
- });
|
|
|
-
|
|
|
- const personsData = searchParams.keyword ? data : allData;
|
|
|
- const isLoadingData = searchParams.keyword ? isLoading : isLoadingAll;
|
|
|
+ const personsData = data;
|
|
|
+ const isLoadingData = isLoading;
|
|
|
|
|
|
// 重置选择器状态
|
|
|
useEffect(() => {
|
|
|
if (!open) {
|
|
|
- setSearchParams({ keyword: '', page: 1, pageSize: 10 });
|
|
|
+ setSearchParams({
|
|
|
+ name: '',
|
|
|
+ gender: '',
|
|
|
+ disabilityId: '',
|
|
|
+ phone: '',
|
|
|
+ disabilityType: '',
|
|
|
+ disabilityLevel: '',
|
|
|
+ page: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ });
|
|
|
setSelectedPersons([]);
|
|
|
setAreaSelection({});
|
|
|
+ setSearchAreaNames({});
|
|
|
setShowBlacklistConfirm(false);
|
|
|
setPendingSelection(null);
|
|
|
}
|
|
|
@@ -114,17 +146,23 @@ const DisabledPersonSelector: React.FC<DisabledPersonSelectorProps> = ({
|
|
|
|
|
|
// 处理搜索
|
|
|
const handleSearch = () => {
|
|
|
- refetch();
|
|
|
+ setSearchParams(prev => ({ ...prev, page: 1 }));
|
|
|
};
|
|
|
|
|
|
// 处理重置搜索
|
|
|
const handleResetSearch = () => {
|
|
|
setSearchParams({
|
|
|
- keyword: '',
|
|
|
+ name: '',
|
|
|
+ gender: '',
|
|
|
+ disabilityId: '',
|
|
|
+ phone: '',
|
|
|
+ disabilityType: '',
|
|
|
+ disabilityLevel: '',
|
|
|
page: 1,
|
|
|
pageSize: 10,
|
|
|
});
|
|
|
setAreaSelection({});
|
|
|
+ setSearchAreaNames({});
|
|
|
};
|
|
|
|
|
|
// 处理选择人员
|
|
|
@@ -234,8 +272,8 @@ const DisabledPersonSelector: React.FC<DisabledPersonSelectorProps> = ({
|
|
|
<Input
|
|
|
id="name"
|
|
|
placeholder="输入姓名"
|
|
|
- value={searchParams.keyword || ''}
|
|
|
- onChange={(e) => setSearchParams(prev => ({ ...prev, keyword: e.target.value }))}
|
|
|
+ value={searchParams.name || ''}
|
|
|
+ onChange={(e) => setSearchParams(prev => ({ ...prev, name: e.target.value }))}
|
|
|
data-testid="search-name-input"
|
|
|
/>
|
|
|
</div>
|
|
|
@@ -243,11 +281,9 @@ const DisabledPersonSelector: React.FC<DisabledPersonSelectorProps> = ({
|
|
|
<div className="space-y-2">
|
|
|
<Label htmlFor="gender">性别</Label>
|
|
|
<Select
|
|
|
- value={searchParams.keyword?.includes('男') ? '男' : searchParams.keyword?.includes('女') ? '女' : ''}
|
|
|
+ value={searchParams.gender || ''}
|
|
|
onValueChange={(value) => {
|
|
|
- if (value) {
|
|
|
- setSearchParams(prev => ({ ...prev, keyword: value }));
|
|
|
- }
|
|
|
+ setSearchParams(prev => ({ ...prev, gender: value }));
|
|
|
}}
|
|
|
>
|
|
|
<SelectTrigger>
|
|
|
@@ -265,8 +301,8 @@ const DisabledPersonSelector: React.FC<DisabledPersonSelectorProps> = ({
|
|
|
<Input
|
|
|
id="disabilityId"
|
|
|
placeholder="输入残疾证号"
|
|
|
- value={searchParams.keyword || ''}
|
|
|
- onChange={(e) => setSearchParams(prev => ({ ...prev, keyword: e.target.value }))}
|
|
|
+ value={searchParams.disabilityId || ''}
|
|
|
+ onChange={(e) => setSearchParams(prev => ({ ...prev, disabilityId: e.target.value }))}
|
|
|
data-testid="search-disability-id-input"
|
|
|
/>
|
|
|
</div>
|
|
|
@@ -276,8 +312,8 @@ const DisabledPersonSelector: React.FC<DisabledPersonSelectorProps> = ({
|
|
|
<Input
|
|
|
id="phone"
|
|
|
placeholder="输入联系电话"
|
|
|
- value={searchParams.keyword || ''}
|
|
|
- onChange={(e) => setSearchParams(prev => ({ ...prev, keyword: e.target.value }))}
|
|
|
+ value={searchParams.phone || ''}
|
|
|
+ onChange={(e) => setSearchParams(prev => ({ ...prev, phone: e.target.value }))}
|
|
|
data-testid="search-phone-input"
|
|
|
/>
|
|
|
</div>
|
|
|
@@ -290,7 +326,20 @@ const DisabledPersonSelector: React.FC<DisabledPersonSelectorProps> = ({
|
|
|
<Form {...form}>
|
|
|
<AreaSelect
|
|
|
value={areaSelection}
|
|
|
- onChange={setAreaSelection}
|
|
|
+ onChange={(value) => {
|
|
|
+ // 转换类型以确保类型兼容
|
|
|
+ setAreaSelection({
|
|
|
+ provinceId: typeof value.provinceId === 'number' ? value.provinceId : undefined,
|
|
|
+ cityId: typeof value.cityId === 'number' ? value.cityId : undefined,
|
|
|
+ districtId: typeof value.districtId === 'number' ? value.districtId : undefined,
|
|
|
+ });
|
|
|
+ // 同时更新用于搜索的地区名称
|
|
|
+ setSearchAreaNames({
|
|
|
+ province: value.provinceId?.toString(),
|
|
|
+ city: value.cityId?.toString(),
|
|
|
+ district: value.districtId?.toString(),
|
|
|
+ });
|
|
|
+ }}
|
|
|
data-testid="area-select"
|
|
|
/>
|
|
|
</Form>
|
|
|
@@ -301,11 +350,9 @@ const DisabledPersonSelector: React.FC<DisabledPersonSelectorProps> = ({
|
|
|
<div className="space-y-2">
|
|
|
<Label htmlFor="disabilityType">残疾类型</Label>
|
|
|
<Select
|
|
|
- value={searchParams.keyword || ''}
|
|
|
+ value={searchParams.disabilityType || ''}
|
|
|
onValueChange={(value) => {
|
|
|
- if (value) {
|
|
|
- setSearchParams(prev => ({ ...prev, keyword: value }));
|
|
|
- }
|
|
|
+ setSearchParams(prev => ({ ...prev, disabilityType: value }));
|
|
|
}}
|
|
|
>
|
|
|
<SelectTrigger>
|
|
|
@@ -326,11 +373,9 @@ const DisabledPersonSelector: React.FC<DisabledPersonSelectorProps> = ({
|
|
|
<div className="space-y-2">
|
|
|
<Label htmlFor="disabilityLevel">残疾等级</Label>
|
|
|
<Select
|
|
|
- value={searchParams.keyword || ''}
|
|
|
+ value={searchParams.disabilityLevel || ''}
|
|
|
onValueChange={(value) => {
|
|
|
- if (value) {
|
|
|
- setSearchParams(prev => ({ ...prev, keyword: value }));
|
|
|
- }
|
|
|
+ setSearchParams(prev => ({ ...prev, disabilityLevel: value }));
|
|
|
}}
|
|
|
>
|
|
|
<SelectTrigger>
|
|
|
@@ -435,7 +480,7 @@ const DisabledPersonSelector: React.FC<DisabledPersonSelectorProps> = ({
|
|
|
)}
|
|
|
<TableCell className="sticky left-0 bg-background z-10">{person.name}</TableCell>
|
|
|
<TableCell>{person.gender}</TableCell>
|
|
|
- <TableCell>{person.idCard}</TableCell>
|
|
|
+ <TableCell>{maskIdCard(person.idCard)}</TableCell>
|
|
|
<TableCell>{person.disabilityId}</TableCell>
|
|
|
<TableCell>{person.phone}</TableCell>
|
|
|
<TableCell className="hidden lg:table-cell">{person.province}</TableCell>
|