|
|
@@ -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"
|
|
|
/>
|