# SearchAdvanced 组件自定义文档

## 概述

SearchAdvanced 是一个高级搜索配置组件，为 Search 组件提供更精细的搜索参数控制功能。该组件在标准搜索模式下显示，包含搜索标签切换和针对不同搜索类型（消息、用户、群组）的高级筛选选项。组件支持消息的时间范围筛选、用户的性别和年龄筛选等功能，通过直观的UI界面让用户能够快速设置搜索条件，提升搜索的精准度和效率。

## Props 配置表格

| 字段 | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| variant | `VariantType` | - | 搜索组件的显示模式 |
| searchType | `SearchTabType` | - | 当前激活的搜索类型 |
| advancedParams | `Map<SearchType, SearchParamsMap[SearchType]>` | - | 高级搜索参数映射 |
| onAdvancedParamsChange | `(type: SearchType, params: SearchParamsMap[SearchType]) => void` | - | 高级搜索参数变化回调 |

## Props 详解

### variant

type: `VariantType`

variant 用于指定搜索组件的显示模式，决定是否显示高级搜索功能，默认值为 `-`。

### searchType

type: `SearchTabType`

searchType 用于指定当前激活的搜索类型（全部、消息、用户、群组），默认值为 `-`。

### advancedParams

type: `Map<SearchType, SearchParamsMap[SearchType]>`

advancedParams 用于存储各种搜索类型的高级参数配置，包含时间范围、性别、年龄等筛选条件，默认值为 `-`。

#### Example1: 基础高级参数配置

```tsx
import { SearchAdvanced } from 'tuikit-atomicx-react';
import { SearchType, VariantType } from 'tuikit-atomicx-react';
import { useState } from 'react';

function SearchWithBasicAdvancedParams() {
  const [searchType, setSearchType] = useState<SearchTabType>('all');
  const [advancedParams, setAdvancedParams] = useState<Map<SearchType, SearchParamsMap[SearchType]>>(
    new Map([
      [SearchType.MESSAGE, {
        timePosition: 0,
        timePeriod: 0,
        conversationID: ''
      }],
      [SearchType.USER, {
        gender: undefined,
        miniBirthday: undefined,
        maxBirthday: undefined
      }],
      [SearchType.GROUP, {}]
    ])
  );

  const handleAdvancedParamsChange = (type: SearchType, params: SearchParamsMap[SearchType]) => {
    setAdvancedParams(prev => {
      const newMap = new Map(prev);
      newMap.set(type, params);
      return newMap;
    });
    
    console.log('高级参数更新:', { type, params });
  };

  return (
    <div style={{ padding: '20px' }}>
      <h3>高级搜索配置</h3>
      
      <SearchAdvanced
        variant={VariantType.STANDARD}
        searchType={searchType}
        advancedParams={advancedParams}
        onAdvancedParamsChange={handleAdvancedParamsChange}
      />
      
      {/* 显示当前参数 */}
      <div style={{ 
        marginTop: '20px', 
        padding: '12px', 
        backgroundColor: '#f5f5f5', 
        borderRadius: '4px' 
      }}>
        <h4>当前搜索参数:</h4>
        <pre style={{ fontSize: '12px' }}>
          {JSON.stringify(Object.fromEntries(advancedParams), null, 2)}
        </pre>
      </div>
    </div>
  );
}
```

#### Example2: 自定义参数预设配置

```tsx
import { SearchAdvanced } from 'tuikit-atomicx-react';
import { SearchType, VariantType } from 'tuikit-atomicx-react';
import { useState, useEffect } from 'react';

function SearchWithPresetAdvancedParams() {
  const [searchType, setSearchType] = useState<SearchTabType>('all');
  const [advancedParams, setAdvancedParams] = useState<Map<SearchType, SearchParamsMap[SearchType]>>(new Map());
  const [activePreset, setActivePreset] = useState<string>('');

  // 预设配置
  const presetConfigs = {
    recentMessages: {
      name: '最近消息',
      type: SearchType.MESSAGE,
      params: {
        timePosition: Math.floor(Date.now() / 1000),
        timePeriod: 7 * 24 * 60 * 60, // 最近7天
        conversationID: ''
      }
    },
    todayMessages: {
      name: '今日消息',
      type: SearchType.MESSAGE,
      params: {
        timePosition: Math.floor(Date.now() / 1000),
        timePeriod: 24 * 60 * 60, // 今天
        conversationID: ''
      }
    },
    maleUsers: {
      name: '男性用户',
      type: SearchType.USER,
      params: {
        gender: 'male',
        miniBirthday: undefined,
        maxBirthday: undefined
      }
    },
    youngUsers: {
      name: '年轻用户',
      type: SearchType.USER,
      params: {
        gender: undefined,
        miniBirthday: ageToBirthday(35), // 35岁以下
        maxBirthday: ageToBirthday(18)  // 18岁以上
      }
    }
  };

  useEffect(() => {
    // 初始化默认参数
    const defaultParams = new Map([
      [SearchType.MESSAGE, { timePosition: 0, timePeriod: 0, conversationID: '' }],
      [SearchType.USER, { gender: undefined, miniBirthday: undefined, maxBirthday: undefined }],
      [SearchType.GROUP, {}]
    ]);
    setAdvancedParams(defaultParams);
  }, []);

  const handleAdvancedParamsChange = (type: SearchType, params: SearchParamsMap[SearchType]) => {
    setAdvancedParams(prev => {
      const newMap = new Map(prev);
      newMap.set(type, params);
      return newMap;
    });
    
    // 检查是否匹配预设
    const matchedPreset = Object.entries(presetConfigs).find(([key, config]) => {
      return config.type === type && JSON.stringify(config.params) === JSON.stringify(params);
    });
    
    setActivePreset(matchedPreset ? matchedPreset[0] : '');
    
    // 保存到本地存储
    localStorage.setItem('searchAdvancedParams', JSON.stringify(Object.fromEntries(advancedParams)));
  };

  const applyPreset = (presetKey: string) => {
    const preset = presetConfigs[presetKey as keyof typeof presetConfigs];
    if (preset) {
      setSearchType(preset.type);
      setAdvancedParams(prev => {
        const newMap = new Map(prev);
        newMap.set(preset.type, preset.params);
        return newMap;
      });
      setActivePreset(presetKey);
    }
  };

  const clearAllParams = () => {
    const clearedParams = new Map([
      [SearchType.MESSAGE, { timePosition: 0, timePeriod: 0, conversationID: '' }],
      [SearchType.USER, { gender: undefined, miniBirthday: undefined, maxBirthday: undefined }],
      [SearchType.GROUP, {}]
    ]);
    setAdvancedParams(clearedParams);
    setActivePreset('');
  };

  return (
    <div style={{ padding: '20px' }}>
      {/* 预设配置按钮 */}
      <div style={{ marginBottom: '20px' }}>
        <h4>快速配置:</h4>
        <div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap' }}>
          {Object.entries(presetConfigs).map(([key, config]) => (
            <button
              key={key}
              onClick={() => applyPreset(key)}
              style={{
                padding: '6px 12px',
                backgroundColor: activePreset === key ? '#007AFF' : '#f0f0f0',
                color: activePreset === key ? 'white' : '#333',
                border: '1px solid #ddd',
                borderRadius: '4px',
                cursor: 'pointer',
                fontSize: '12px'
              }}
            >
              {config.name}
            </button>
          ))}
          <button
            onClick={clearAllParams}
            style={{
              padding: '6px 12px',
              backgroundColor: '#ff4444',
              color: 'white',
              border: 'none',
              borderRadius: '4px',
              cursor: 'pointer',
              fontSize: '12px'
            }}
          >
            清除所有
          </button>
        </div>
      </div>

      <SearchAdvanced
        variant={VariantType.STANDARD}
        searchType={searchType}
        advancedParams={advancedParams}
        onAdvancedParamsChange={handleAdvancedParamsChange}
      />
    </div>
  );
}

// 辅助函数：年龄转生日
function ageToBirthday(age: number): number {
  const currentYear = new Date().getFullYear();
  const birthYear = currentYear - age;
  return new Date(birthYear, 0, 1).getTime() / 1000;
}
```

### onAdvancedParamsChange

type: `(type: SearchType, params: SearchParamsMap[SearchType]) => void`

onAdvancedParamsChange 用于处理高级搜索参数变化的回调函数，当用户修改筛选条件时触发，默认值为 `-`。

#### Example1: 基础参数变化处理

```tsx
import { SearchAdvanced } from 'tuikit-atomicx-react';
import { SearchType, VariantType } from 'tuikit-atomicx-react';
import { useState } from 'react';

function SearchWithBasicParamsHandling() {
  const [searchType, setSearchType] = useState<SearchTabType>(SearchType.MESSAGE);
  const [advancedParams, setAdvancedParams] = useState<Map<SearchType, SearchParamsMap[SearchType]>>(new Map());
  const [searchHistory, setSearchHistory] = useState<Array<{
    type: SearchType;
    params: any;
    timestamp: number;
  }>>([]);

  const handleAdvancedParamsChange = (type: SearchType, params: SearchParamsMap[SearchType]) => {
    console.log('参数变化:', { type, params });
    
    // 更新参数状态
    setAdvancedParams(prev => {
      const newMap = new Map(prev);
      newMap.set(type, params);
      return newMap;
    });
    
    // 记录搜索历史
    setSearchHistory(prev => [
      {
        type,
        params,
        timestamp: Date.now()
      },
      ...prev.slice(0, 9) // 保留最近10条记录
    ]);
    
    // 参数验证
    if (type === SearchType.MESSAGE) {
      const messageParams = params as SearchParamsMap[SearchType.MESSAGE];
      if (messageParams.timePeriod && messageParams.timePeriod > 365 * 24 * 60 * 60) {
        showWarningToast('时间范围不能超过一年');
      }
    }
    
    if (type === SearchType.USER) {
      const userParams = params as SearchParamsMap[SearchType.USER];
      if (userParams.miniBirthday && userParams.maxBirthday && 
          userParams.miniBirthday < userParams.maxBirthday) {
        showWarningToast('年龄范围设置有误');
      }
    }
    
    // 触发搜索
    triggerSearch(type, params);
  };

  const triggerSearch = (type: SearchType, params: SearchParamsMap[SearchType]) => {
    // 根据参数执行搜索
    console.log('执行搜索:', { type, params });
    
    // 这里可以调用实际的搜索API
    // searchAPI.search(type, params);
  };

  const formatParamsForDisplay = (type: SearchType, params: any) => {
    switch (type) {
      case SearchType.MESSAGE:
        if (params.timePeriod > 0) {
          const days = Math.floor(params.timePeriod / (24 * 60 * 60));
          return `最近${days}天的消息`;
        }
        return '所有消息';
      
      case SearchType.USER:
        const conditions = [];
        if (params.gender) {
          conditions.push(`性别: ${params.gender === 'male' ? '男' : '女'}`);
        }
        if (params.miniBirthday || params.maxBirthday) {
          const minAge = params.maxBirthday ? birthdayToAge(params.maxBirthday) : 0;
          const maxAge = params.miniBirthday ? birthdayToAge(params.miniBirthday) : 99;
          conditions.push(`年龄: ${minAge}-${maxAge}岁`);
        }
        return conditions.length > 0 ? conditions.join(', ') : '无限制';
      
      default:
        return '默认搜索';
    }
  };

  return (
    <div style={{ padding: '20px' }}>
      <SearchAdvanced
        variant={VariantType.STANDARD}
        searchType={searchType}
        advancedParams={advancedParams}
        onAdvancedParamsChange={handleAdvancedParamsChange}
      />
      
      {/* 当前搜索条件显示 */}
      <div style={{
        marginTop: '16px',
        padding: '12px',
        backgroundColor: '#e3f2fd',
        borderRadius: '4px'
      }}>
        <h4 style={{ margin: '0 0 8px 0' }}>当前搜索条件:</h4>
        <p style={{ margin: 0, fontSize: '14px' }}>
          {formatParamsForDisplay(searchType, advancedParams.get(searchType) || {})}
        </p>
      </div>
      
      {/* 搜索历史 */}
      {searchHistory.length > 0 && (
        <div style={{
          marginTop: '16px',
          padding: '12px',
          backgroundColor: '#f5f5f5',
          borderRadius: '4px'
        }}>
          <h4 style={{ margin: '0 0 8px 0' }}>搜索历史:</h4>
          {searchHistory.slice(0, 3).map((item, index) => (
            <div key={index} style={{ fontSize: '12px', color: '#666', marginBottom: '4px' }}>
              {new Date(item.timestamp).toLocaleTimeString()} - {formatParamsForDisplay(item.type, item.params)}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

function birthdayToAge(timestamp: number): number {
  const birthDate = new Date(timestamp * 1000);
  const today = new Date();
  let age = today.getFullYear() - birthDate.getFullYear();
  const monthDiff = today.getMonth() - birthDate.getMonth();
  if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
    age--;
  }
  return age;
}
```

#### Example2: 复杂参数变化处理和持久化

```tsx
import { SearchAdvanced } from 'tuikit-atomicx-react';
import { SearchType, VariantType } from 'tuikit-atomicx-react';
import { useState, useEffect, useCallback } from 'react';

function SearchWithAdvancedParamsHandling() {
  const [searchType, setSearchType] = useState<SearchTabType>('all');
  const [advancedParams, setAdvancedParams] = useState<Map<SearchType, SearchParamsMap[SearchType]>>(new Map());
  const [paramValidation, setParamValidation] = useState<Record<string, string>>({});
  const [searchStats, setSearchStats] = useState({
    totalSearches: 0,
    messageSearches: 0,
    userSearches: 0,
    groupSearches: 0
  });

  // 从本地存储加载参数
  useEffect(() => {
    const savedParams = localStorage.getItem('advancedSearchParams');
    const savedStats = localStorage.getItem('searchStats');
    
    if (savedParams) {
      try {
        const parsedParams = JSON.parse(savedParams);
        const paramsMap = new Map(Object.entries(parsedParams));
        setAdvancedParams(paramsMap);
      } catch (error) {
        console.error('加载搜索参数失败:', error);
      }
    }
    
    if (savedStats) {
      try {
        setSearchStats(JSON.parse(savedStats));
      } catch (error) {
        console.error('加载搜索统计失败:', error);
      }
    }
  }, []);

  const validateParams = useCallback((type: SearchType, params: SearchParamsMap[SearchType]): string[] => {
    const errors: string[] = [];
    
    switch (type) {
      case SearchType.MESSAGE:
        const messageParams = params as SearchParamsMap[SearchType.MESSAGE];
        if (messageParams.timePeriod && messageParams.timePeriod < 0) {
          errors.push('时间范围不能为负数');
        }
        if (messageParams.timePeriod && messageParams.timePeriod > 365 * 24 * 60 * 60) {
          errors.push('时间范围不能超过一年');
        }
        if (messageParams.timePosition && messageParams.timePosition > Math.floor(Date.now() / 1000)) {
          errors.push('结束时间不能晚于当前时间');
        }
        break;
        
      case SearchType.USER:
        const userParams = params as SearchParamsMap[SearchType.USER];
        if (userParams.miniBirthday && userParams.maxBirthday && 
            userParams.miniBirthday < userParams.maxBirthday) {
          errors.push('最小年龄不能大于最大年龄');
        }
        if (userParams.miniBirthday) {
          const minAge = birthdayToAge(userParams.miniBirthday);
          if (minAge < 0 || minAge > 150) {
            errors.push('年龄范围不合理');
          }
        }
        break;
        
      default:
        break;
    }
    
    return errors;
  }, []);

  const handleAdvancedParamsChange = useCallback((type: SearchType, params: SearchParamsMap[SearchType]) => {
    console.log('高级参数变化:', { type, params });
    
    // 参数验证
    const validationErrors = validateParams(type, params);
    setParamValidation(prev => ({
      ...prev,
      [type]: validationErrors.join('; ')
    }));
    
    if (validationErrors.length > 0) {
      showErrorToast(`参数验证失败: ${validationErrors.join('; ')}`);
      return;
    }
    
    // 更新参数
    setAdvancedParams(prev => {
      const newMap = new Map(prev);
      newMap.set(type, params);
      
      // 持久化到本地存储
      const paramsObject = Object.fromEntries(newMap);
      localStorage.setItem('advancedSearchParams', JSON.stringify(paramsObject));
      
      return newMap;
    });
    
    // 更新搜索统计
    setSearchStats(prev => {
      const newStats = {
        ...prev,
        totalSearches: prev.totalSearches + 1,
        [`${type.toLowerCase()}Searches`]: (prev[`${type.toLowerCase()}Searches` as keyof typeof prev] as number) + 1
      };
      localStorage.setItem('searchStats', JSON.stringify(newStats));
      return newStats;
    });
    
    // 发送分析事件
    analytics.track('advanced_search_params_changed', {
      searchType: type,
      paramsCount: Object.keys(params).filter(key => params[key as keyof typeof params] !== undefined).length,
      timestamp: new Date().toISOString()
    });
    
    // 延迟执行搜索（防抖）
    debounceSearch(type, params);
  }, [validateParams]);

  // 防抖搜索
  const debounceSearch = useCallback(
    debounce((type: SearchType, params: SearchParamsMap[SearchType]) => {
      executeSearch(type, params);
    }, 500),
    []
  );

  const executeSearch = async (type: SearchType, params: SearchParamsMap[SearchType]) => {
    try {
      console.log('执行搜索:', { type, params });
      
      // 这里调用实际的搜索API
      // const results = await searchAPI.advancedSearch(type, params);
      // onSearchComplete?.(results);
      
      showSuccessToast('搜索参数已更新');
    } catch (error) {
      console.error('搜索失败:', error);
      showErrorToast('搜索失败，请重试');
    }
  };

  const resetAllParams = () => {
    const defaultParams = new Map([
      [SearchType.MESSAGE, { timePosition: 0, timePeriod: 0, conversationID: '' }],
      [SearchType.USER, { gender: undefined, miniBirthday: undefined, maxBirthday: undefined }],
      [SearchType.GROUP, {}]
    ]);
    
    setAdvancedParams(defaultParams);
    setParamValidation({});
    localStorage.removeItem('advancedSearchParams');
  };

  const exportSearchConfig = () => {
    const config = {
      params: Object.fromEntries(advancedParams),
      stats: searchStats,
      timestamp: new Date().toISOString()
    };
    
    const blob = new Blob([JSON.stringify(config, null, 2)], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'search-config.json';
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div style={{ padding: '20px' }}>
      {/* 控制面板 */}
      <div style={{
        display: 'flex',
        gap: '12px',
        marginBottom: '20px',
        padding: '12px',
        backgroundColor: '#f8f9fa',
        borderRadius: '4px'
      }}>
        <button onClick={resetAllParams} style={{ padding: '6px 12px', fontSize: '12px' }}>
          重置参数
        </button>
        <button onClick={exportSearchConfig} style={{ padding: '6px 12px', fontSize: '12px' }}>
          导出配置
        </button>
        <div style={{ marginLeft: 'auto', fontSize: '12px', color: '#666' }}>
          总搜索次数: {searchStats.totalSearches}
        </div>
      </div>

      <SearchAdvanced
        variant={VariantType.STANDARD}
        searchType={searchType}
        advancedParams={advancedParams}
        onAdvancedParamsChange={handleAdvancedParamsChange}
      />
      
      {/* 参数验证错误显示 */}
      {Object.entries(paramValidation).map(([type, error]) => (
        error && (
          <div key={type} style={{
            marginTop: '8px',
            padding: '8px 12px',
            backgroundColor: '#ffebee',
            color: '#c62828',
            borderRadius: '4px',
            fontSize: '12px'
          }}>
            {type}: {error}
          </div>
        )
      ))}
      
      {/* 搜索统计 */}
      <div style={{
        marginTop: '16px',
        padding: '12px',
        backgroundColor: '#e8f5e8',
        borderRadius: '4px',
        fontSize: '12px'
      }}>
        <h4 style={{ margin: '0 0 8px 0' }}>搜索统计:</h4>
        <div>消息搜索: {searchStats.messageSearches} 次</div>
        <div>用户搜索: {searchStats.userSearches} 次</div>
        <div>群组搜索: {searchStats.groupSearches} 次</div>
      </div>
    </div>
  );
}

// 工具函数
function debounce(func: Function, wait: number) {
  let timeout: NodeJS.Timeout;
  return function executedFunction(...args: any[]) {
    const later = () => {
      clearTimeout(timeout);
      func(...args);
    };
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
  };
}

function birthdayToAge(timestamp: number): number {
  const birthDate = new Date(timestamp * 1000);
  const today = new Date();
  let age = today.getFullYear() - birthDate.getFullYear();
  const monthDiff = today.getMonth() - birthDate.getMonth();
  if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
    age--;
  }
  return age;
} 