import 'package:asset_assistant/pages/country_add_page.dart'; import 'package:asset_assistant/utils/host_utils.dart'; import 'package:flutter/material.dart'; import 'package:dio/dio.dart'; // 国家数据模型(flag字段存储emoji) class Country { final String countryId; final String name; final String code; final String? flag; // 国旗字段,存储emoji Country({ required this.countryId, required this.name, required this.code, this.flag, }); // 从JSON构建对象 factory Country.fromJson(Map json) { return Country( countryId: json['country_id'], name: json['name'], code: json['code'], flag: json['flag'], // 解析emoji字段 ); } } // 接口响应模型 class CountryResponse { final bool success; final String message; final CountryData data; CountryResponse({ required this.success, required this.message, required this.data, }); factory CountryResponse.fromJson(Map json) { return CountryResponse( success: json['success'], message: json['message'], data: CountryData.fromJson(json['data']), ); } } // 响应数据模型 class CountryData { final int total; final int page; final int pageSize; final List items; CountryData({ required this.total, required this.page, required this.pageSize, required this.items, }); factory CountryData.fromJson(Map json) { var itemsList = json['items'] as List? ?? []; List items = itemsList.map((i) => Country.fromJson(i)).toList(); return CountryData( total: json['total'], page: json['page'], pageSize: json['page_size'], items: items, ); } } class CountryPage extends StatefulWidget { const CountryPage({super.key}); @override State createState() => _CountryPageState(); } class _CountryPageState extends State { List _countries = []; bool _isLoading = true; String? _errorMessage; int _currentPage = 1; final int _pageSize = 20; bool _hasMoreData = true; late ScrollController _scrollController; @override void initState() { super.initState(); _scrollController = ScrollController(); _scrollController.addListener(_onScroll); _fetchCountries(); } @override void dispose() { _scrollController.dispose(); super.dispose(); } void _onScroll() { if (_isLoading) return; if (_hasMoreData && _scrollController.position.pixels >= _scrollController.position.maxScrollExtent - 200) { _loadMore(); } } Future _fetchCountries({bool isRefresh = false}) async { if (isRefresh) { setState(() { _currentPage = 1; _hasMoreData = true; }); } if (!_hasMoreData && !isRefresh) return; setState(() { _isLoading = true; }); try { final baseUrl = HostUtils().currentHost; final path = '/country/read'; final url = '$baseUrl$path'; debugPrint('====== 开始请求国家列表 ======'); debugPrint('请求URL: $url'); debugPrint('请求参数: page=$_currentPage, page_size=$_pageSize'); final dio = Dio(); final response = await dio.post( url, data: { 'page': _currentPage, 'page_size': _pageSize, 'name': '', 'code': '', 'country_id': '', 'flag': '', }, options: Options( headers: {'Content-Type': 'application/x-www-form-urlencoded'}, ), ); debugPrint('请求成功,状态码: ${response.statusCode}'); debugPrint('响应数据: ${response.data}'); if (response.statusCode == 200) { final CountryResponse countryResponse = CountryResponse.fromJson( response.data, ); if (countryResponse.success) { setState(() { if (isRefresh) { _countries = countryResponse.data.items; } else { _countries.addAll(countryResponse.data.items); } _hasMoreData = _countries.length < countryResponse.data.total; _currentPage++; _errorMessage = null; }); debugPrint('数据解析成功,当前列表总数: ${_countries.length}'); debugPrint('是否还有更多数据: $_hasMoreData,下一页: $_currentPage'); } else { setState(() { _errorMessage = countryResponse.message; }); debugPrint('接口返回失败: ${countryResponse.message}'); } } else { setState(() { _errorMessage = '服务器响应异常: ${response.statusCode}'; }); debugPrint('服务器响应异常: ${response.statusCode}'); } } on DioException catch (e) { String errorMsg = '网络请求失败'; if (e.response != null) { errorMsg = '请求失败: ${e.response?.statusCode}'; debugPrint( '请求错误,状态码: ${e.response?.statusCode},响应数据: ${e.response?.data}', ); } else if (e.type == DioExceptionType.connectionTimeout) { errorMsg = '连接超时,请检查网络'; debugPrint('连接超时: ${e.message}'); } else if (e.type == DioExceptionType.connectionError) { errorMsg = '网络连接错误'; debugPrint('网络连接错误: ${e.message}'); } else { debugPrint('Dio异常: ${e.type},消息: ${e.message}'); } setState(() { _errorMessage = errorMsg; }); } catch (e) { setState(() { _errorMessage = '发生未知错误: $e'; }); debugPrint('未知错误: $e'); } finally { setState(() { _isLoading = false; }); debugPrint('====== 请求结束 ======\n'); } } Future _refresh() async { debugPrint('触发下拉刷新'); await _fetchCountries(isRefresh: true); } void _loadMore() { if (!_isLoading && _hasMoreData) { debugPrint('触发加载更多,当前页: $_currentPage'); _fetchCountries(); } } @override Widget build(BuildContext context) { final theme = Theme.of(context); return Scaffold( appBar: AppBar( title: const Text('国家列表'), centerTitle: true, elevation: 4, shadowColor: Colors.black12, backgroundColor: theme.colorScheme.surfaceContainerHighest, leading: IconButton( icon: const Icon(Icons.arrow_back), onPressed: () { Navigator.of(context).pop(); }, ), actions: [ IconButton( icon: const Icon(Icons.add), onPressed: () async { debugPrint('点击新增按钮,跳转到新增页面'); final result = await Navigator.push( context, MaterialPageRoute(builder: (context) => const AddCountryPage()), ); if (result == true) { debugPrint('从新增页面返回,刷新列表数据'); _fetchCountries(isRefresh: true); } }, ), ], ), body: SafeArea(child: _buildBody(theme)), ); } Widget _buildBody(ThemeData theme) { // 加载中且列表为空 if (_isLoading && _countries.isEmpty) { return const Center(child: CircularProgressIndicator()); } // 有错误信息且列表为空 if (_errorMessage != null && _countries.isEmpty) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( _errorMessage!, style: TextStyle(color: theme.colorScheme.error), ), const SizedBox(height: 16), ElevatedButton( onPressed: () => _fetchCountries(isRefresh: true), child: const Text('重试'), ), ], ), ); } // 无数据状态(列表为空且无错误) if (_countries.isEmpty) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // 无数据背景图标 Icon( Icons.flag_outlined, size: 120, color: theme.colorScheme.onSurface.withOpacity(0.1), ), const SizedBox(height: 24), // 无数据提示文字 Text( '暂无国家数据', style: theme.textTheme.titleLarge?.copyWith( color: theme.colorScheme.onSurface.withOpacity(0.5), ), ), const SizedBox(height: 16), // 刷新按钮 IconButton( icon: Icon( Icons.refresh, size: 28, color: theme.colorScheme.primary, ), onPressed: () => _fetchCountries(isRefresh: true), tooltip: '刷新数据', ), ], ), ); } // 正常列表展示 return RefreshIndicator( onRefresh: _refresh, child: ListView.builder( padding: const EdgeInsets.symmetric(vertical: 8), itemCount: _countries.length + (_hasMoreData ? 1 : 0), itemBuilder: (context, index) { if (index < _countries.length) { final country = _countries[index]; return _buildCountryItem(theme, country); } else { return Padding( padding: const EdgeInsets.symmetric(vertical: 16), child: Center( child: _isLoading ? const CircularProgressIndicator() : const Text('没有更多数据了'), ), ); } }, controller: _scrollController, ), ); } Widget _buildCountryItem(ThemeData theme, Country country) { return Column( mainAxisSize: MainAxisSize.min, children: [ Material( color: Colors.transparent, child: InkWell( onTap: () { // 可以添加点击事件 }, borderRadius: BorderRadius.circular(8), splashColor: theme.colorScheme.primary.withAlpha(26), highlightColor: theme.colorScheme.primary.withAlpha(13), child: Container( height: 64, padding: const EdgeInsets.symmetric(horizontal: 16), child: Row( children: [ // 国旗Emoji展示区域 Container( width: 40, height: 40, decoration: BoxDecoration( color: theme.colorScheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(8), ), child: Center( child: Text( // 显示国旗emoji,如果没有则显示默认图标 country.flag != null && country.flag!.isNotEmpty ? country.flag! : '', style: const TextStyle(fontSize: 24), // 适当调整emoji大小 ), ), ), const SizedBox(width: 16), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( country.name, style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w500, color: theme.colorScheme.onSurface, ), ), const SizedBox(height: 4), Text( '代码: ${country.code}', style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.onSurfaceVariant, ), ), ], ), ), Icon( Icons.arrow_forward_ios, size: 18, color: theme.hintColor, ), ], ), ), ), ), Divider( height: 1, thickness: 1, indent: 72, endIndent: 16, color: theme.dividerColor, ), ], ); } }