import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:loopin/service/http.dart'; import 'package:loopin/components/my_toast.dart'; import '../../behavior/custom_scroll_behavior.dart'; class SearchResultPage extends StatefulWidget { const SearchResultPage({super.key}); @override State createState() => _SearchResultPageState(); } class _SearchResultPageState extends State with SingleTickerProviderStateMixin { late TabController _tabController; String _searchQuery = Get.parameters?['searchWords'] ?? ''; int _initialTabIndex = 0; final TextEditingController _searchController = TextEditingController(); final FocusNode _searchFocusNode = FocusNode(); // 三个tab的数据 List _videoResults = []; List _productResults = []; List _userResults = []; bool _isLoading = true; @override void initState() { super.initState(); // 初始化搜索控制器 _searchController.text = _searchQuery; // 解析tab参数 final tabParam = Get.parameters?['tab']; if (tabParam != null && tabParam.isNotEmpty) { _initialTabIndex = int.tryParse(tabParam) ?? 0; } _tabController = TabController( length: 4, vsync: this, initialIndex: _initialTabIndex, ); // 监听Tab切换 _tabController.addListener(_onTabChanged); // 加载初始数据 _loadInitialData(); } @override void dispose() { _tabController.removeListener(_onTabChanged); _tabController.dispose(); _searchController.dispose(); _searchFocusNode.dispose(); super.dispose(); } void _onTabChanged() { if (_tabController.indexIsChanging) { // 加载对应Tab的数据 _loadTabData(_tabController.index); } } // 执行搜索 void _performSearch() { final newQuery = _searchController.text.trim(); if (newQuery.isEmpty) { MyToast().tip( title: '请输入搜索关键词', position: 'center', type: 'warning', ); return; } if (newQuery == _searchQuery) { return; // 搜索关键词相同,不需要重新搜索 } setState(() { _searchQuery = newQuery; _isLoading = true; }); // 关闭键盘 _searchFocusNode.unfocus(); // 重新加载数据 _loadInitialData(); } // 加载初始数据 Future _loadInitialData() async { try { setState(() { _isLoading = true; }); // 加载第一个Tab的数据 await _loadTabData(_initialTabIndex); } catch (e) { print('加载数据失败: $e'); MyToast().tip( title: '加载失败', position: 'center', type: 'error', ); } finally { setState(() { _isLoading = false; }); } } // 加载指定Tab的数据 Future _loadTabData(int tabIndex) async { try { setState(() { _isLoading = true; }); switch (tabIndex) { case 0: // 视频 final res = await Http.get('/api/search/videos', params: { 'keyword': _searchQuery, 'page': 1, 'current': 20, }); if (res['code'] == 200) { setState(() { _videoResults = res['data']['list'] ?? []; }); } break; case 1: // 商品 final res = await Http.get('/api/search/products', params: { 'keyword': _searchQuery, 'page': 1, 'current': 20, }); if (res['code'] == 200) { setState(() { _productResults = res['data']['list'] ?? []; }); } break; case 2: // 用户 final res = await Http.get('/api/search/users', params: { 'keyword': _searchQuery, 'page': 1, 'current': 20, }); if (res['code'] == 200) { setState(() { _userResults = res['data']['list'] ?? []; }); } break; } } catch (e) { print('加载Tab数据失败: $e'); MyToast().tip( title: '加载失败', position: 'center', type: 'error', ); } finally { setState(() { _isLoading = false; }); } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, appBar: AppBar( title: Container( height: 36, decoration: BoxDecoration( color: Colors.grey[800], borderRadius: BorderRadius.circular(18), ), child: Row( children: [ const SizedBox(width: 12), const Icon(Icons.search, color: Colors.grey, size: 20), const SizedBox(width: 8), Expanded( child: TextField( controller: _searchController, focusNode: _searchFocusNode, style: const TextStyle( color: Colors.white, fontSize: 14, height: 1.2, ), decoration: const InputDecoration( hintText: '搜索视频、商品、用户、团购...', hintStyle: TextStyle( color: Colors.grey, fontSize: 14, height: 1.2, ), border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 8), isDense: true, ), onSubmitted: (_) => _performSearch(), ), ), IconButton( icon: const Icon(Icons.close, color: Colors.grey, size: 18), onPressed: () { _searchController.clear(); }, padding: const EdgeInsets.all(4), constraints: const BoxConstraints(), ), const SizedBox(width: 4), ], ), ), backgroundColor: Colors.black, foregroundColor: Colors.white, elevation: 0.5, actions: [ TextButton( onPressed: _performSearch, child: const Text( '搜索', style: TextStyle( color: Colors.white, fontSize: 14, fontWeight: FontWeight.bold, ), ), ), const SizedBox(width: 8), ], ), body: Column( children: [ Container( color: Colors.white, padding: const EdgeInsets.only(top: 8), child: TabBar( controller: _tabController, // isScrollable: true, indicatorColor: Colors.pink, labelColor: Colors.pink, unselectedLabelColor: Colors.grey, labelStyle: const TextStyle(fontWeight: FontWeight.bold), unselectedLabelStyle: const TextStyle(fontWeight: FontWeight.normal), padding: const EdgeInsets.only(left: 16), // 添加左侧内边距 indicatorPadding: EdgeInsets.zero, labelPadding: const EdgeInsets.symmetric(horizontal: 16), // 保持标签间距 indicatorSize: TabBarIndicatorSize.label, // 改为label宽度,与文字一样宽 dividerColor: Colors.transparent, tabs: const [ Tab(text: '视频'), Tab(text: '商品'), Tab(text: '用户'), ], ), ), // Tab内容区域 Expanded( child: _isLoading ? const Center(child: CircularProgressIndicator()) : TabBarView( controller: _tabController, children: [ // 视频Tab _buildVideoTab(), // 商品Tab _buildProductTab(), // 用户Tab _buildUserTab(), ], ), ), ], ), ); } // 视频Tab Widget _buildVideoTab() { if (_videoResults.isEmpty) { return _buildEmptyView('暂无视频结果'); } return ScrollConfiguration( behavior: CustomScrollBehavior().copyWith(scrollbars: false), child: ListView.builder( padding: const EdgeInsets.all(12), itemCount: _videoResults.length, itemBuilder: (context, index) { final video = _videoResults[index] as Map; return _buildVideoItem(video); }, ), ); } Widget _buildVideoItem(Map video) { return Container( margin: const EdgeInsets.only(bottom: 12), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 视频封面 Container( width: 120, height: 80, decoration: BoxDecoration( borderRadius: BorderRadius.circular(6), color: Colors.grey[200], image: video['cover'] != null ? DecorationImage( image: NetworkImage(video['cover'].toString()), fit: BoxFit.cover, ) : null, ), child: video['cover'] == null ? const Icon(Icons.videocam, color: Colors.grey) : null, ), const SizedBox(width: 12), // 视频信息 Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( video['title']?.toString() ?? '无标题', style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 4), Text( video['author']?.toString() ?? '未知作者', style: const TextStyle( fontSize: 12, color: Colors.grey, ), ), const SizedBox(height: 4), Row( children: [ Text( '#${video['tag']?.toString() ?? '无标签'}', style: const TextStyle( fontSize: 12, color: Colors.blue, ), ), const SizedBox(width: 8), Text( video['location']?.toString() ?? '', style: const TextStyle( fontSize: 12, color: Colors.grey, ), ), ], ), ], ), ), ], ), ); } // 商品Tab Widget _buildProductTab() { if (_productResults.isEmpty) { return _buildEmptyView('暂无商品结果'); } return ScrollConfiguration( behavior: CustomScrollBehavior().copyWith(scrollbars: false), child: ListView.builder( padding: const EdgeInsets.all(12), itemCount: _productResults.length, itemBuilder: (context, index) { final product = _productResults[index] as Map; return _buildProductItem(product); }, ), ); } Widget _buildProductItem(Map product) { return Container( margin: const EdgeInsets.only(bottom: 12), padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.1), blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: Row( children: [ // 商品图片 Container( width: 80, height: 80, decoration: BoxDecoration( borderRadius: BorderRadius.circular(6), color: Colors.grey[200], image: product['image'] != null ? DecorationImage( image: NetworkImage(product['image'].toString()), fit: BoxFit.cover, ) : null, ), child: product['image'] == null ? const Icon(Icons.shopping_bag, color: Colors.grey) : null, ), const SizedBox(width: 12), // 商品信息 Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( product['name']?.toString() ?? '未知商品', style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 6), Text( '¥${product['price']?.toString() ?? '0.00'}', style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Colors.pink, ), ), const SizedBox(height: 6), Row( children: [ Text( '已售 ${product['sold']?.toString() ?? '0'}', style: const TextStyle( fontSize: 12, color: Colors.grey, ), ), const SizedBox(width: 12), Text( '${product['comments']?.toString() ?? '0'}条评论', style: const TextStyle( fontSize: 12, color: Colors.grey, ), ), ], ), ], ), ), // 购买按钮 IconButton( onPressed: () { // 跳转到商品详情 }, icon: const Icon(Icons.shopping_cart, size: 20), ), ], ), ); } // 用户Tab Widget _buildUserTab() { if (_userResults.isEmpty) { return _buildEmptyView('暂无用户结果'); } return ScrollConfiguration( behavior: CustomScrollBehavior().copyWith(scrollbars: false), child: ListView.builder( padding: const EdgeInsets.all(12), itemCount: _userResults.length, itemBuilder: (context, index) { final user = _userResults[index] as Map; return _buildUserItem(user); }, ), ); } Widget _buildUserItem(Map user) { return Container( margin: const EdgeInsets.only(bottom: 12), padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.1), blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: Row( children: [ // 用户头像 Container( width: 50, height: 50, decoration: BoxDecoration( shape: BoxShape.circle, color: Colors.grey[200], image: user['avatar'] != null ? DecorationImage( image: NetworkImage(user['avatar'].toString()), fit: BoxFit.cover, ) : null, ), child: user['avatar'] == null ? const Icon(Icons.person, color: Colors.grey) : null, ), const SizedBox(width: 12), // 用户信息 Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( user['name']?.toString() ?? '未知用户', style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), ), const SizedBox(height: 4), Text( '粉丝: ${user['fans']?.toString() ?? '0'}', style: const TextStyle( fontSize: 12, color: Colors.grey, ), ), ], ), ), // 关注按钮 ElevatedButton( onPressed: () { // 关注用户 }, style: ElevatedButton.styleFrom( backgroundColor: Colors.pink, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), ), child: const Text( '关注', style: TextStyle(fontSize: 12), ), ), ], ) ); } // 空视图 Widget _buildEmptyView(String message) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.search_off, size: 60, color: Colors.grey), const SizedBox(height: 16), Text( message, style: const TextStyle( fontSize: 16, color: Colors.grey, ), ), ], ), ); } }