Просмотр исходного кода

fix: 修复公司管理和银行名称管理的软删除过滤问题

- 修复公司管理模块 findByPlatform 和 findOne 缺少 status 过滤
- 修复银行名称模块缺少 defaultFilters 配置
- 修复银行名称管理组件查询返回 null 的问题
- 在公司管理表单中添加启用/禁用状态配置
- 添加输入法中文输入支持(防抖处理)
- 清理未使用的变量和导入

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname 5 часов назад
Родитель
Сommit
9142cf0585

+ 54 - 5
allin-packages/company-management-ui/src/components/CompanyManagement.tsx

@@ -3,6 +3,7 @@ import { useQuery, useMutation } from '@tanstack/react-query';
 import { Plus, Edit, Trash2, Search } from 'lucide-react';
 import { format } from 'date-fns';
 import { Input } from '@d8d/shared-ui-components/components/ui/input';
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@d8d/shared-ui-components/components/ui/select';
 import { Button } from '@d8d/shared-ui-components/components/ui/button';
 import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@d8d/shared-ui-components/components/ui/card';
 import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@d8d/shared-ui-components/components/ui/table';
@@ -46,7 +47,8 @@ const CompanyManagement: React.FC = () => {
       contactPerson: undefined,
       contactPhone: undefined,
       contactEmail: undefined,
-      address: undefined
+      address: undefined,
+      status: 1
     }
   });
 
@@ -181,12 +183,13 @@ const CompanyManagement: React.FC = () => {
     setIsCreateForm(false);
     updateForm.reset({
       id: company.id,
-      platformId: company.platformId,
+      platformId: company.platformId || undefined,
       companyName: company.companyName,
       contactPerson: company.contactPerson || undefined,
       contactPhone: company.contactPhone || undefined,
       contactEmail: company.contactEmail || undefined,
-      address: company.address || undefined
+      address: company.address || undefined,
+      status: company.status ?? 1
     });
     setIsModalOpen(true);
   };
@@ -368,7 +371,6 @@ const CompanyManagement: React.FC = () => {
                           value={field.value}
                           onChange={field.onChange}
                           placeholder="选择平台"
-                          testId="create-company-platform-selector"
                         />
                       </FormControl>
                       <FormMessage />
@@ -461,6 +463,30 @@ const CompanyManagement: React.FC = () => {
                     </FormItem>
                   )}
                 />
+                <FormField
+                  control={createForm.control}
+                  name="status"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>状态</FormLabel>
+                      <Select
+                        value={field.value?.toString()}
+                        onValueChange={(value) => field.onChange(parseInt(value, 10))}
+                      >
+                        <FormControl>
+                          <SelectTrigger data-testid="create-company-status-select">
+                            <SelectValue placeholder="选择状态" />
+                          </SelectTrigger>
+                        </FormControl>
+                        <SelectContent>
+                          <SelectItem value="1" data-testid="create-company-status-enabled">启用</SelectItem>
+                          <SelectItem value="0" data-testid="create-company-status-disabled">禁用</SelectItem>
+                        </SelectContent>
+                      </Select>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
                 <DialogFooter>
                   <Button
                     type="button"
@@ -494,7 +520,6 @@ const CompanyManagement: React.FC = () => {
                           value={field.value}
                           onChange={field.onChange}
                           placeholder="选择平台"
-                          testId="edit-company-platform-selector"
                         />
                       </FormControl>
                       <FormMessage />
@@ -587,6 +612,30 @@ const CompanyManagement: React.FC = () => {
                     </FormItem>
                   )}
                 />
+                <FormField
+                  control={updateForm.control}
+                  name="status"
+                  render={({ field }) => (
+                    <FormItem>
+                      <FormLabel>状态</FormLabel>
+                      <Select
+                        value={field.value?.toString()}
+                        onValueChange={(value) => field.onChange(parseInt(value, 10))}
+                      >
+                        <FormControl>
+                          <SelectTrigger data-testid="edit-company-status-select">
+                            <SelectValue placeholder="选择状态" />
+                          </SelectTrigger>
+                        </FormControl>
+                        <SelectContent>
+                          <SelectItem value="1" data-testid="edit-company-status-enabled">启用</SelectItem>
+                          <SelectItem value="0" data-testid="edit-company-status-disabled">禁用</SelectItem>
+                        </SelectContent>
+                      </Select>
+                      <FormMessage />
+                    </FormItem>
+                  )}
+                />
                 <DialogFooter>
                   <Button
                     type="button"

+ 8 - 0
allin-packages/company-module/src/schemas/company.schema.ts

@@ -72,6 +72,10 @@ export const CreateCompanySchema = z.object({
   address: z.string().max(200, '地址不能超过200个字符').optional().transform((val) => val === '' ? undefined : val).openapi({
     description: '地址',
     example: '北京市朝阳区'
+  }),
+  status: z.number().int().min(0).max(1).default(1).optional().openapi({
+    description: '状态:1-正常,0-禁用',
+    example: 1
   })
 });
 
@@ -104,6 +108,10 @@ export const UpdateCompanySchema = z.object({
   address: z.string().max(200, '地址不能超过200个字符').optional().transform((val) => val === '' ? undefined : val).openapi({
     description: '地址',
     example: '北京市朝阳区'
+  }),
+  status: z.number().int().min(0).max(1).optional().openapi({
+    description: '状态:1-正常,0-禁用',
+    example: 1
   })
 });
 

+ 50 - 3
packages/bank-name-management-ui/src/components/BankNameManagement.tsx

@@ -1,4 +1,4 @@
-import { useState } from 'react'
+import { useState, useRef, useCallback, useEffect } from 'react'
 import { useQuery, useMutation } from '@tanstack/react-query'
 import { Plus, Search, Edit, Trash2, X } from 'lucide-react'
 import { format } from 'date-fns'
@@ -22,6 +22,23 @@ import { bankNameClientManager } from '../api/bankNameClient'
 import type { BankName, BankNameFormData, BankNameQueryParams } from '../types/bankName'
 import { CreateBankNameDto, UpdateBankNameDto } from '@d8d/bank-names-module/schemas'
 
+// 防抖 Hook
+const useDebounce = <T extends (...args: any[]) => any>(
+  callback: T,
+  delay: number
+): T => {
+  const timeoutRef = useRef<ReturnType<typeof setTimeout>>()
+
+  return useCallback((...args: Parameters<T>) => {
+    if (timeoutRef.current) {
+      clearTimeout(timeoutRef.current)
+    }
+    timeoutRef.current = setTimeout(() => {
+      callback(...args)
+    }, delay)
+  }, [callback, delay]) as T
+}
+
 // 表单Schema直接使用后端定义
 const createFormSchema = CreateBankNameDto
 const updateFormSchema = UpdateBankNameDto
@@ -29,6 +46,10 @@ const updateFormSchema = UpdateBankNameDto
 export const BankNameManagement = () => {
   // 状态管理
   const [searchParams, setSearchParams] = useState<BankNameQueryParams>({ page: 1, limit: 10, search: '', status: 'all' })
+  // 本地搜索输入状态 - 不触发查询
+  const [searchInput, setSearchInput] = useState('')
+  // 输入法输入状态 - 使用 ref 确保同步更新
+  const isComposingRef = useRef(false)
   const [isModalOpen, setIsModalOpen] = useState(false)
   const [editingType, setEditingType] = useState<BankName | null>(null)
   const [isCreateForm, setIsCreateForm] = useState(true)
@@ -80,6 +101,11 @@ export const BankNameManagement = () => {
     }
   })
 
+  // 当 searchParams.search 变化时(如点击搜索按钮后),同步输入框
+  useEffect(() => {
+    setSearchInput(searchParams.search || '')
+  }, [searchParams.search])
+
   // 创建mutation
   const createMutation = useMutation({
     mutationFn: async (data: BankNameFormData) => {
@@ -142,6 +168,14 @@ export const BankNameManagement = () => {
     }
   })
 
+  // 防抖更新搜索参数
+  const debouncedUpdateSearch = useDebounce((value: string) => {
+    // 只在非输入法状态下更新搜索参数(使用 ref 确保读取最新值)
+    if (!isComposingRef.current) {
+      setSearchParams(prev => ({ ...prev, search: value, page: 1 }))
+    }
+  }, 300)
+
   // 业务逻辑函数
   const handleSearch = () => {
     setSearchParams(prev => ({ ...prev, page: 1 }))
@@ -274,8 +308,21 @@ export const BankNameManagement = () => {
                 <Search className="absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" />
                 <Input
                   placeholder="搜索类型名称或调用别名..."
-                  value={searchParams.search}
-                  onChange={(e) => setSearchParams(prev => ({ ...prev, search: e.target.value }))}
+                  value={searchInput}
+                  onChange={(e) => {
+                    const value = e.target.value
+                    setSearchInput(value)
+                    // 只在非输入法状态下触发防抖更新(使用 ref 确保读取最新值)
+                    if (!isComposingRef.current) {
+                      debouncedUpdateSearch(value)
+                    }
+                  }}
+                  onCompositionStart={() => { isComposingRef.current = true }}
+                  onCompositionEnd={(e) => {
+                    isComposingRef.current = false
+                    // 输入法结束后,使用最终输入的值触发搜索
+                    debouncedUpdateSearch(e.currentTarget.value)
+                  }}
                   className="pl-8"
                   data-testid="search-input"
                 />

+ 24 - 3
packages/user-management-ui/src/components/UserManagement.tsx

@@ -415,7 +415,18 @@ export const UserManagement = () => {
                           })}
                         >
                           {roleId === 1 ? '管理员' : '普通用户'}
-                          <X className="h-3 w-3" />
+                          <X
+                            className="h-3 w-3 cursor-pointer pointer-events-auto"
+                            onClick={() => handleFilterChange({
+                              roleIds: filters.roleIds.filter(id => id !== roleId)
+                            })}
+                          />
+                          <X
+                            className="h-3 w-3 cursor-pointer pointer-events-auto"
+                            onClick={() => handleFilterChange({
+                              roleIds: filters.roleIds.filter(id => id !== roleId)
+                            })}
+                          />
                         </Badge>
                       ))}
                     </div>
@@ -485,8 +496,18 @@ export const UserManagement = () => {
                     })}
                   >
                     角色: {roleId === 1 ? '管理员' : '普通用户'}
-                    <X className="h-3 w-3" />
-                  </Badge>
+                          <X
+                            className="h-3 w-3 cursor-pointer pointer-events-auto"
+                            onClick={() => handleFilterChange({
+                              roleIds: filters.roleIds.filter(id => id !== roleId)
+                            })}
+                          />
+                    <X
+                      className="h-3 w-3 cursor-pointer pointer-events-auto"
+                      onClick={() => handleFilterChange({
+                        roleIds: filters.roleIds.filter(id => id !== roleId)
+                      })}
+                    />
                 ))}
                 {filters.createdAt && (
                   <Badge variant="secondary" className="flex items-center gap-1 overflow-visible">