---
description: "检查并修正筛选表单指令"
---

# 筛选表单检查与修复指南

## 问题描述

在使用 React Hook Form 和状态管理进行筛选表单开发时，常见的同步问题包括：

1. **表单状态与查询参数不同步**：重置操作只清除查询参数，但表单字段值保持不变
2. **表单验证问题**：字段验证规则不完整或错误
3. **UI状态不一致**：筛选条件显示与实际查询条件不符

## 检查步骤

### 1. 检查表单重置功能
```bash
# 检查 handleReset 函数是否同时重置表单和查询参数
grep -n "handleReset" src/**/*.tsx
```

### 2. 检查表单字段同步
```bash
# 检查表单字段是否与查询参数正确映射
grep -n "setSearchParams\|form.reset" src/**/*.tsx
```

### 3. 验证表单字段定义
```bash
# 检查所有表单字段是否正确定义
grep -n "FormField\|control" src/**/*.tsx | head -10
```

## 常见问题及修复方案

### 问题1: 重置功能不完整

**症状**：点击重置按钮后，查询参数被清除但表单字段值保持不变

**修复方案**：
```typescript
// 错误示例 - 只重置查询参数
const handleReset = () => {
  setSearchParams({
    status: undefined,
    keyword: undefined,
    // ...其他参数
  });
};

// 正确示例 - 同时重置表单和查询参数
const handleReset = () => {
  // 重置表单字段值
  form.reset({
    status: undefined,
    keyword: undefined,
    dateRange: undefined,
    // ...所有表单字段
  });
  
  // 重置搜索参数
  setSearchParams({
    status: undefined,
    keyword: undefined,
    startDate: undefined,
    endDate: undefined,
    // ...对应查询参数
  });
};
```

### 问题2: Select组件缺少"全部"选项

**症状**：筛选表单中的Select组件缺少"全部"选项，用户无法取消筛选

**修复方案**：
```typescript
// 错误示例 - 缺少全部选项
<SelectContent>
  {Object.values(StatusEnum).map(status => (
    <SelectItem key={status} value={status}>
      {StatusNameMap[status]}
    </SelectItem>
  ))}
</SelectContent>

// 正确示例 - 添加全部选项（使用特殊值"all"并转换为undefined）
<Select onValueChange={(value) => field.onChange(value === "all" ? undefined : value)} value={field.value || "all"}>
  <FormControl>
    <SelectTrigger>
      <SelectValue placeholder="全部状态" />
    </SelectTrigger>
  </FormControl>
  <SelectContent>
    <SelectItem value="all">全部状态</SelectItem>
    {Object.values(StatusEnum).map(status => (
      <SelectItem key={status} value={status}>
        {StatusNameMap[status]}
      </SelectItem>
    ))}
  </SelectContent>
</Select>
```

**注意事项**：
- Radix UI Select组件不允许使用空字符串作为SelectItem的value
- 使用特殊值"all"来表示全部选项，在onChange时转换为undefined
- 在value prop中使用 `field.value || "all"` 来确保选中状态正确显示

### 问题2: 表单字段映射错误

**症状**：表单字段名称与查询参数名称不一致

**修复方案**：
```typescript
// 确保表单字段名与查询参数名正确映射
const handleSearch = (values: any) => {
  setSearchParams({
    status: values.status,           // 表单字段名 -> 查询参数名
    keyword: values.keyword,         // 保持一致
    startDate: values.dateRange?.[0]?.toISOString(),  // 复杂映射
    endDate: values.dateRange?.[1]?.toISOString(),
  });
};
```

### 问题3: 初始状态不一致

**症状**：组件挂载时表单初始值与查询参数不匹配

**修复方案**：
```typescript
// 使用 useEffect 同步初始状态
useEffect(() => {
  const initialValues = {
    status: searchParams.status || undefined,
    keyword: searchParams.keyword || undefined,
    dateRange: searchParams.startDate && searchParams.endDate 
      ? [new Date(searchParams.startDate), new Date(searchParams.endDate)]
      : undefined,
  };
  form.reset(initialValues);
}, []);
```

## 通用CRUD filters参数使用指南

### 问题4: 未使用通用CRUD的filters参数

**症状**：页面使用独立的查询参数而不是通用CRUD的标准filters参数

**修复方案**：
```typescript
// 错误示例 - 使用独立参数
const res = await client.$get({
  query: {
    page: 1,
    pageSize: 10,
    status: searchParams.status,
    problemType: searchParams.problemType,
    keyword: searchParams.keyword,
    startDate: searchParams.startDate,
    endDate: searchParams.endDate,
  }
});

// 正确示例 - 使用通用CRUD filters参数
const filters: any = {};
if (searchParams.status) filters.status = searchParams.status;
if (searchParams.problemType) filters.problemType = searchParams.problemType;
if (searchParams.startDate && searchParams.endDate) {
  filters.createdAt = {
    between: [searchParams.startDate, searchParams.endDate]
  };
}

const res = await client.$get({
  query: {
    page: 1,
    pageSize: 10,
    keyword: searchParams.keyword,  // 独立的关键词参数
    filters: Object.keys(filters).length > 0 ? JSON.stringify(filters) : undefined,
  }
});
```

### 问题5: 关联字段搜索与筛选不支持

**症状**：无法通过关联实体的字段进行搜索和筛选（如设备名称、用户名称等）

**修复方案**：

1. **配置searchFields和relations**:
```typescript
const routes = createCrudRoutes({
  entity: YourEntity,
  searchFields: ['title', 'description', 'device.zichanInfo.assetName'], // 支持关联字段搜索（格式：relation.field 或 relation.nestedRelation.field）
  relations: ['device.zichanInfo'], // 确保关联数据被加载
  // ...其他配置
});
```

2. **使用关联字段筛选**:
```typescript
// 在filters参数中使用关联字段进行精确筛选
const filters = {
  'device.zichanInfo.assetName': '服务器001',           // 精确匹配
  'device.zichanInfo.status': { gte: 1 },              // 范围查询
  'device.zichanInfo.department.name': '%IT%'          // 模糊匹配
};

const res = await client.$get({
  query: {
    page: 1,
    pageSize: 10,
    filters: JSON.stringify(filters)
  }
});
```

**别名生成规则**：
- 单级关联：`device.zichanInfo` → 别名：`device_zichanInfo`
- 嵌套关联：`device.zichanInfo.department` → 别名：`device_zichanInfo_department`
- 字段引用：`device_zichanInfo.assetName`, `device_zichanInfo_department.name`

**支持的筛选操作**：
- 精确匹配：`{ "relation.field": "value" }`
- 模糊匹配：`{ "relation.field": "%value%" }`
- 范围查询：`{ "relation.field": { "gte": min, "lte": max } }`
- IN查询：`{ "relation.field": [value1, value2, ...] }`

## 完整代码示例

### 修复后的筛选表单组件（使用通用CRUD filters）

```typescript
import React from 'react';
import { useForm } from 'react-hook-form';
import { Form, FormField, FormItem, FormLabel, FormControl } from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Button } from '@/components/ui/button';
import { workOrderClient } from '@/client/api';

export function FilterForm() {
  const [searchParams, setSearchParams] = useState({});
  const form = useForm();

  // 搜索处理 - 使用通用CRUD filters参数
  const handleSearch = (values: any) => {
    // 构建filters对象
    const filters: any = {};
    if (values.status) filters.status = values.status;
    if (values.problemType) filters.problemType = values.problemType;
    if (values.dateRange?.[0] && values.dateRange?.[1]) {
      filters.createdAt = {
        between: [values.dateRange[0], values.dateRange[1]]
      };
    }

    setSearchParams({
      keyword: values.keyword,
      filters: Object.keys(filters).length > 0 ? JSON.stringify(filters) : undefined,
    });
  };

  // 重置处理 - 修复后的版本
  const handleReset = () => {
    // 重置表单字段值
    form.reset({
      status: undefined,
      keyword: undefined,
      problemType: undefined,
      dateRange: undefined,
    });
    
    // 重置搜索参数
    setSearchParams({
      keyword: undefined,
      filters: undefined,
    });
  };

  return (
    <Form {...form}>
      <form onSubmit={form.handleSubmit(handleSearch)}>
        <FormField
          control={form.control}
          name="status"
          render={({ field }) => (
            <FormItem>
              <FormLabel>状态</FormLabel>
              <Select onValueChange={(value) => field.onChange(value === "all" ? undefined : value)} value={field.value || "all"}>
                <FormControl>
                  <SelectTrigger>
                    <SelectValue placeholder="选择状态" />
                  </SelectTrigger>
                </FormControl>
                <SelectContent>
                  <SelectItem value="all">全部状态</SelectItem>
                  <SelectItem value="active">活跃</SelectItem>
                  <SelectItem value="inactive">非活跃</SelectItem>
                </SelectContent>
              </Select>
            </FormItem>
          )}
        />
        
        <FormField
          control={form.control}
          name="keyword"
          render={({ field }) => (
            <FormItem>
              <FormLabel>关键字</FormLabel>
              <FormControl>
                <Input placeholder="输入关键字" {...field} />
              </FormControl>
            </FormItem>
          )}
        />
        
        <div className="flex gap-2">
          <Button type="submit">查询</Button>
          <Button type="button" variant="outline" onClick={handleReset}>
            重置
          </Button>
        </div>
      </form>
    </Form>
  );
}
```

## 最佳实践

1. **保持同步**：始终确保表单状态与查询参数同步
2. **完整重置**：重置操作应同时清除表单字段和查询参数
3. **类型安全**：为表单值和查询参数定义明确的 TypeScript 类型
4. **测试验证**：编写测试用例验证重置功能正常工作
5. **用户体验**：重置后应自动触发数据重新加载

## 自动化检查脚本

```bash
#!/bin/bash
# 检查筛选表单常见问题

echo "=== 筛选表单检查 ==="

# 检查 handleReset 函数
echo "1. 检查 handleReset 函数:"
grep -A 10 -B 2 "handleReset" src/**/*.tsx | grep -E "setSearchParams|form.reset"

# 检查表单字段映射
echo "2. 检查表单字段映射:"
grep -A 5 -B 5 "handleSearch" src/**/*.tsx

# 检查初始状态同步
echo "3. 检查初始状态同步:"
grep -A 10 -B 2 "useEffect.*searchParams" src/**/*.tsx

# 检查通用CRUD filters参数使用
echo "4. 检查通用CRUD filters参数使用:"
grep -A 5 -B 5 "filters.*JSON.stringify" src/**/*.tsx

# 检查关联字段搜索配置
echo "5. 检查关联字段搜索配置:"
grep -A 3 -B 3 "searchFields.*\." src/server/api/**/index.ts

echo "=== 检查完成 ==="
```

## 最佳实践更新

6. **标准接口**：优先使用通用CRUD的filters参数而不是独立参数
7. **关联搜索**：合理配置searchFields和relations支持关联字段搜索
8. **性能优化**：避免在filters中使用过多关联查询，必要时使用自定义服务方法

使用此指令可以快速识别和修复筛选表单中的常见同步问题，包括通用CRUD filters参数的使用和关联字段搜索功能。

使用此指令可以快速识别和修复筛选表单中的常见同步问题。