From 7a237fe804a4ff11c4c8e38e7da547973205a8ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=AD=90=E8=B4=A4?= Date: Fri, 3 Apr 2026 12:08:06 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/controller/home/home_controller.dart | 309 ++++++++++++++---- .../setting/blacklist_controller.dart | 6 +- lib/model/home/check_black_data.dart | 18 + .../home/friend_footprint_info_data.dart | 15 + .../home/friend_footprint_list_data.dart | 92 ++++++ lib/network/api_urls.dart | 4 +- lib/network/home_api.dart | 4 +- lib/network/home_api.g.dart | 36 +- lib/network/user_api.dart | 6 + lib/network/user_api.g.dart | 33 ++ lib/pages/home/content_card.dart | 34 +- lib/pages/home/friend_footprint_page.dart | 153 +++++++++ lib/pages/home/recommend_tab.dart | 120 ++++--- lib/pages/mine/mine_page.dart | 26 +- lib/pages/mine/real_feedback_page.dart | 2 +- lib/pages/setting/blacklist_page.dart | 4 +- 16 files changed, 716 insertions(+), 146 deletions(-) create mode 100644 lib/model/home/check_black_data.dart create mode 100644 lib/model/home/friend_footprint_info_data.dart create mode 100644 lib/model/home/friend_footprint_list_data.dart create mode 100644 lib/pages/home/friend_footprint_page.dart diff --git a/lib/controller/home/home_controller.dart b/lib/controller/home/home_controller.dart index fafc259..4539193 100644 --- a/lib/controller/home/home_controller.dart +++ b/lib/controller/home/home_controller.dart @@ -16,12 +16,16 @@ class HomeController extends GetxController { // 同城列表数据 final nearbyFeed = [].obs; final nearbyDisFeed = [].obs; + final friendFootFeed = [].obs; // 推荐列表的加载状态和分页信息 final recommendIsLoading = false.obs; final recommendPage = 1.obs; final recommendHasMore = true.obs; - + final friendHasMore = true.obs; + final friendIsLoading = false.obs; + final friendPage = 1.obs; + // 同城列表的加载状态和分页信息 final nearbyIsLoading = false.obs; final nearbyPage = 1.obs; @@ -48,6 +52,8 @@ class HomeController extends GetxController { Map? _location; CityInfo? _cityInfo; + final friendFootprintInfo = [].obs; + Timer? _timer; @override void onInit() { super.onInit(); @@ -55,19 +61,49 @@ class HomeController extends GetxController { _homeApi = Get.find(); // 初始化时加载数据 loadInitialData(); + getFriendFootprintInfo(); + _timer = Timer.periodic(const Duration(minutes: 1), (timer) { + + getFriendFootprintInfo(); + }); + } + @override + void onClose() { + super.onClose(); + _timer?.cancel(); } + getFriendFootprintInfo() async { + try { + final response = await _homeApi.userGetFriendFootprintInfo(); + if (response.data.isSuccess && response.data.data != null) { + friendFootprintInfo.value = response.data.data?.profilePhotoList ?? []; + update(); + } else { + // 响应失败,抛出异常 + throw Exception(response.data.message ?? '获取数据失败'); + } + } catch(e){ + print('钱包数据获取失败: $e'); + SmartDialog.showToast('钱包数据获取失败'); + rethrow; + + } + } Future _fetchLocation() async { _status = 'Fetching location...'; try { + print(1); final location = await _locationPlugin.getCurrentLocation(); + print(2); + print(location); if (location == null) { _status = 'Location unavailable'; @@ -94,6 +130,9 @@ class HomeController extends GetxController { _cityStatus = 'Skip city lookup'; _cityInfo = null; + print(_status); + print(_cityStatus); + } } @@ -112,6 +151,8 @@ class HomeController extends GetxController { print(_cityInfo!.code); print("_cityInfo.code"); + loadNearbyInitialData(); + loadNearbyDisInitialData(); } on CityInfoLookupException catch (e) { @@ -211,34 +252,68 @@ class HomeController extends GetxController { if(cityCode.value == null || cityCode.value == 0){ await _fetchLocation(); + } else { + if (nearbyIsLoading.value) return; + + try { + nearbyIsLoading.value = true; + nearbyPage.value = 1; + nearbyHasMore.value = true; + + // 获取同城数据 (type=1) + final result = await _fetchMarriageData( + pageNum: 1, + type: 2, + code: districtCode.value + ); + + // 重置并更新同城列表 + nearbyDisFeed.clear(); + nearbyDisFeed.addAll(result['records']); + + // 根据返回的分页信息判断是否还有更多数据 + final int currentPage = result['current'] ?? 1; + // final int totalPages = result['pages'] ?? 1; + final int totalPages = 100; + nearbyHasMore.value = currentPage < totalPages; + } catch (e) { + _handleError('获取同城列表异常', e, '同城列表加载失败,请稍后重试'); + } finally { + nearbyIsLoading.value = false; + } } - if (nearbyIsLoading.value) return; + + } + + /// 加载推荐列表初始数据 + Future loadFriendFootInitialData() async { + if (friendIsLoading.value) return; try { - nearbyIsLoading.value = true; - nearbyPage.value = 1; - nearbyHasMore.value = true; + friendIsLoading.value = true; + friendPage.value = 1; + friendHasMore.value = true; - // 获取同城数据 (type=1) + // 获取推荐数据 (type=0) final result = await _fetchMarriageData( - pageNum: 1, - type: 2, - code: districtCode.value + pageNum: 1, + type: 5, ); - // 重置并更新同城列表 - nearbyDisFeed.clear(); - nearbyDisFeed.addAll(result['records']); + // 重置并更新推荐列表 + friendFootFeed.clear(); + friendFootFeed.addAll(result['records']); // 根据返回的分页信息判断是否还有更多数据 final int currentPage = result['current'] ?? 1; // final int totalPages = result['pages'] ?? 1; final int totalPages = 100; - nearbyHasMore.value = currentPage < totalPages; + friendHasMore.value = currentPage < totalPages; + print(friendHasMore.value); } catch (e) { - _handleError('获取同城列表异常', e, '同城列表加载失败,请稍后重试'); + _handleError('获取推荐列表异常', e, '推荐列表加载失败,请稍后重试'); } finally { - nearbyIsLoading.value = false; + friendIsLoading.value = false; } } @@ -360,6 +435,41 @@ class HomeController extends GetxController { } } + /// 加载推荐列表更多数据 + Future loadFriendFootMoreData() async { + if (friendIsLoading.value || !friendHasMore.value) return; + + try { + friendIsLoading.value = true; + // final nextPage = recommendPage.value + 1; + final nextPage = friendPage.value; + print('推荐列表加载更多 - 当前页: ${friendPage.value}, 下一页: $nextPage'); + + // 获取推荐数据 (type=0) + final result = await _fetchMarriageData( + pageNum: nextPage, + type: 5, + ); + + // 更新页码 + friendPage.value = nextPage; + + // 更新推荐列表 + friendFootFeed.addAll(result['records']); + + // 根据返回的分页信息判断是否还有更多数据 + final int currentPage = result['current'] as int; + // final int totalPages = result['pages'] as int; + final int totalPages = 100; + friendHasMore.value = currentPage < totalPages; + print('推荐列表加载更多完成 - 当前页: $currentPage, 总页数: $totalPages, 还有更多: ${friendHasMore.value}'); + } catch (e) { + _handleError('加载推荐更多异常', e, '加载更多失败'); + } finally { + friendIsLoading.value = false; + } + } + /// 刷新数据 Future refreshData([int? tabIndex]) async { final targetTab = tabIndex ?? selectedTabIndex.value; @@ -468,6 +578,37 @@ class HomeController extends GetxController { } } + /// 刷新推荐列表数据 + Future refreshFriendData() async { + if (friendIsLoading.value) return; + + try { + friendIsLoading.value = true; + friendPage.value = 1; + friendHasMore.value = true; + + // 获取推荐数据 (type=0) + final result = await _fetchMarriageData( + pageNum: 1, + type: 5, + ); + + // 更新推荐列表 + friendFootFeed.clear(); + friendFootFeed.addAll(result['records']); + + // 根据返回的分页信息判断是否还有更多数据 + final int currentPage = result['current'] ?? 1; + // final int totalPages = result['pages'] ?? 1; + final int totalPages = 100; + friendHasMore.value = currentPage < totalPages; + } catch (e) { + _handleError('刷新推荐数据异常', e, '刷新失败,请稍后重试'); + } finally { + friendIsLoading.value = false; + } + } + /// 设置当前标签页 void setSelectedTabIndex(int index) async { print('Setting selected tab index to: $index'); @@ -475,10 +616,14 @@ class HomeController extends GetxController { // 确保UI能够更新 update(); if(index == 2 && nearbyFeed.isEmpty){ + print(cityCode.value == null || cityCode.value == 0); + print("cityCode.value == null || cityCode.value == 0"); if(cityCode.value == null || cityCode.value == 0){ - await _fetchLocation(); + _fetchLocation(); + } else { + + loadNearbyInitialData(); } - loadNearbyInitialData(); } } @@ -495,51 +640,101 @@ class HomeController extends GetxController { int? code }) async { try { - print('_fetchMarriageData - pageNum: $pageNum, pageSize: $pageSize, type: $type'); - // 调用API获取数据 - var response = await _homeApi.getMarriageList( - pageNum: pageNum, - pageSize: pageSize, - type: type, - cityCode: type == 1 ? code : null, - districtCode: type == 2 ? code : null, - ); + if(type != 5){ + print('_fetchMarriageData - pageNum: $pageNum, pageSize: $pageSize, type: $type'); + // 调用API获取数据 + var response = await _homeApi.getMarriageList( + pageNum: pageNum, + pageSize: pageSize, + type: type, + cityCode: type == 1 ? code : null, + districtCode: type == 2 ? code : null, + ); + + if (response.data.isSuccess) { + final paginatedData = response.data.data; + + // 检查data是否为空 + if (paginatedData == null) { + return { + 'records': [], + 'current': pageNum, + 'pages': 1, + 'total': 0, + 'size': pageSize, + }; + } + + // data 是 PaginatedResponse,直接使用其属性 + // records 中的每个项是 dynamic,需要转换为 MarriageData + final allRecords = paginatedData.records + .map((item) => MarriageData.fromJson(item as Map)) + .toList(); + + // 过滤掉直播类型的项 + final records = allRecords.where((item) => !item.isLive).toList(); + + print('_fetchMarriageData 返回 - 请求页码: $pageNum, 返回当前页: ${paginatedData.current}, 总页数: ${paginatedData.pages}, 原始记录数: ${allRecords.length}, 过滤后记录数: ${records.length}'); - if (response.data.isSuccess) { - final paginatedData = response.data.data; - - // 检查data是否为空 - if (paginatedData == null) { return { - 'records': [], - 'current': pageNum, - 'pages': 1, - 'total': 0, - 'size': pageSize, + 'records': records, + 'current': paginatedData.current, + 'pages': paginatedData.pages, + 'total': paginatedData.total, + 'size': paginatedData.size, }; + } else { + // 响应失败,抛出异常 + throw Exception(response.data.message); } - - // data 是 PaginatedResponse,直接使用其属性 - // records 中的每个项是 dynamic,需要转换为 MarriageData - final allRecords = paginatedData.records - .map((item) => MarriageData.fromJson(item as Map)) - .toList(); - - // 过滤掉直播类型的项 - final records = allRecords.where((item) => !item.isLive).toList(); - - print('_fetchMarriageData 返回 - 请求页码: $pageNum, 返回当前页: ${paginatedData.current}, 总页数: ${paginatedData.pages}, 原始记录数: ${allRecords.length}, 过滤后记录数: ${records.length}'); - - return { - 'records': records, - 'current': paginatedData.current, - 'pages': paginatedData.pages, - 'total': paginatedData.total, - 'size': paginatedData.size, - }; } else { - // 响应失败,抛出异常 - throw Exception(response.data.message); + print('_fetchMarriageData - pageNum: $pageNum, pageSize: $pageSize, type: $type'); + // 调用API获取数据 + var response = await _homeApi.userPageFriendFootprint( + pageNum: pageNum, + pageSize: pageSize, + ); + + if (response.data.isSuccess) { + final paginatedData = response.data.data; + + // 检查data是否为空 + if (paginatedData == null) { + return { + 'records': [], + 'current': pageNum, + 'pages': 1, + 'total': 0, + 'size': pageSize, + }; + } + + paginatedData.records.forEach((e){ + e["liveStreaming"] = true; + e["isLiveChannelId"] = e["channelId"]; + }); + // data 是 PaginatedResponse,直接使用其属性 + // records 中的每个项是 dynamic,需要转换为 MarriageData + final allRecords = paginatedData.records + .map((item) => MarriageData.fromJson(item as Map)) + .toList(); + + // 过滤掉直播类型的项 + final records = allRecords.where((item) => !item.isLive).toList(); + + print('_fetchMarriageData 返回 - 请求页码: $pageNum, 返回当前页: ${paginatedData.current}, 总页数: ${paginatedData.pages}, 原始记录数: ${allRecords.length}, 过滤后记录数: ${records.length}'); + + return { + 'records': records, + 'current': paginatedData.current, + 'pages': paginatedData.pages, + 'total': paginatedData.total, + 'size': paginatedData.size, + }; + } else { + // 响应失败,抛出异常 + throw Exception(response.data.message); + } } } catch (e) { // 向上抛出异常,让调用方处理 diff --git a/lib/controller/setting/blacklist_controller.dart b/lib/controller/setting/blacklist_controller.dart index 8c65062..3f8d9bc 100644 --- a/lib/controller/setting/blacklist_controller.dart +++ b/lib/controller/setting/blacklist_controller.dart @@ -58,15 +58,15 @@ class BlacklistController extends GetxController { } } - unBlack(String blackUserId) async { + unBlack(String id) async { try { final response = await _userApi.userDeleteUserBlacklist({ - "blackUserId": blackUserId, + "id": id, }); if (response.data.isSuccess) { SmartDialog.showToast('已解除拉黑'); - blackList.value = blackList.where((e) => e.blackUserId != blackUserId).toList(); + blackList.value = blackList.where((e) => e.id != id).toList(); } else { // 响应失败,抛出异常 diff --git a/lib/model/home/check_black_data.dart b/lib/model/home/check_black_data.dart new file mode 100644 index 0000000..af6c545 --- /dev/null +++ b/lib/model/home/check_black_data.dart @@ -0,0 +1,18 @@ +class CheckBlackData { + bool? activeBlack; + bool? passiveBlack; + + CheckBlackData({this.activeBlack, this.passiveBlack}); + + CheckBlackData.fromJson(Map json) { + activeBlack = json['activeBlack']; + passiveBlack = json['passiveBlack']; + } + + Map toJson() { + final Map data = new Map(); + data['activeBlack'] = this.activeBlack; + data['passiveBlack'] = this.passiveBlack; + return data; + } +} diff --git a/lib/model/home/friend_footprint_info_data.dart b/lib/model/home/friend_footprint_info_data.dart new file mode 100644 index 0000000..a65df9f --- /dev/null +++ b/lib/model/home/friend_footprint_info_data.dart @@ -0,0 +1,15 @@ +class FriendFootprintInfoData { + List? profilePhotoList; + + FriendFootprintInfoData({this.profilePhotoList}); + + FriendFootprintInfoData.fromJson(Map json) { + profilePhotoList = json['profilePhotoList'].cast(); + } + + Map toJson() { + final Map data = new Map(); + data['profilePhotoList'] = this.profilePhotoList; + return data; + } +} diff --git a/lib/model/home/friend_footprint_list_data.dart b/lib/model/home/friend_footprint_list_data.dart new file mode 100644 index 0000000..09a8cf9 --- /dev/null +++ b/lib/model/home/friend_footprint_list_data.dart @@ -0,0 +1,92 @@ +class FriendFootprintListData { + List? records; + int? total; + int? size; + int? current; + int? pages; + + FriendFootprintListData( + {this.records, this.total, this.size, this.current, this.pages}); + + FriendFootprintListData.fromJson(Map json) { + if (json['records'] != null) { + records = []; + json['records'].forEach((v) { + records!.add(new Records.fromJson(v)); + }); + } + total = json['total']; + size = json['size']; + current = json['current']; + pages = json['pages']; + } + + Map toJson() { + final Map data = new Map(); + if (this.records != null) { + data['records'] = this.records!.map((v) => v.toJson()).toList(); + } + data['total'] = this.total; + data['size'] = this.size; + data['current'] = this.current; + data['pages'] = this.pages; + return data; + } +} + +class Records { + String? channelId; + String? nickName; + String? profilePhoto; + int? genderCode; + String? birthYear; + String? birthDate; + int? age; + int? provinceCode; + String? provinceName; + int? cityCode; + String? cityName; + + Records( + {this.channelId, + this.nickName, + this.profilePhoto, + this.genderCode, + this.birthYear, + this.birthDate, + this.age, + this.provinceCode, + this.provinceName, + this.cityCode, + this.cityName}); + + Records.fromJson(Map json) { + channelId = json['channelId']; + nickName = json['nickName']; + profilePhoto = json['profilePhoto']; + genderCode = json['genderCode']; + birthYear = json['birthYear']; + birthDate = json['birthDate']; + age = json['age']; + provinceCode = json['provinceCode']; + provinceName = json['provinceName']; + cityCode = json['cityCode']; + cityName = json['cityName']; + } + + Map toJson() { + final Map data = new Map(); + data['channelId'] = this.channelId; + data['nickName'] = this.nickName; + data['profilePhoto'] = this.profilePhoto; + data['genderCode'] = this.genderCode; + data['birthYear'] = this.birthYear; + data['birthDate'] = this.birthDate; + data['age'] = this.age; + data['provinceCode'] = this.provinceCode; + data['provinceName'] = this.provinceName; + data['cityCode'] = this.cityCode; + data['cityName'] = this.cityName; + return data; + } +} diff --git a/lib/network/api_urls.dart b/lib/network/api_urls.dart index 626f086..3be49e1 100644 --- a/lib/network/api_urls.dart +++ b/lib/network/api_urls.dart @@ -109,7 +109,9 @@ class ApiUrls { static const String userGetFriendFootprintInfo = 'dating-agency-chat-audio/user/get/friend-footprint-info'; static const String userPageFriendFootprint = - '/dating-agency-chat-audio/user/page/friend-footprint'; + 'dating-agency-chat-audio/user/page/friend-footprint'; + static const String userCheckUserBlacklist = + 'dating-agency-chat-audio/user/check/user-blacklist'; //首页相关接口 static const String getMarriageList = diff --git a/lib/network/home_api.dart b/lib/network/home_api.dart index 11cbbb2..6e7e5db 100644 --- a/lib/network/home_api.dart +++ b/lib/network/home_api.dart @@ -1,3 +1,5 @@ +import 'package:dating_touchme_app/model/home/friend_footprint_info_data.dart'; +import 'package:dating_touchme_app/model/home/friend_footprint_list_data.dart' hide Records; import 'package:dating_touchme_app/model/home/post_comment_data.dart' hide Records; import 'package:dating_touchme_app/model/home/post_data.dart'; import 'package:dating_touchme_app/model/home/trend_data.dart' hide Records; @@ -91,7 +93,7 @@ abstract class HomeApi { @GET(ApiUrls.userGetFriendFootprintInfo) - Future>>> userGetFriendFootprintInfo(); + Future>> userGetFriendFootprintInfo(); @GET(ApiUrls.userPageFriendFootprint) Future>>> userPageFriendFootprint({ diff --git a/lib/network/home_api.g.dart b/lib/network/home_api.g.dart index 4de6132..ae36f34 100644 --- a/lib/network/home_api.g.dart +++ b/lib/network/home_api.g.dart @@ -457,30 +457,32 @@ class _HomeApi implements HomeApi { } @override - Future>>> + Future>> userGetFriendFootprintInfo() async { final _extra = {}; final queryParameters = {}; final _headers = {}; const Map? _data = null; - final _options = _setStreamType>>>( - Options(method: 'GET', headers: _headers, extra: _extra) - .compose( - _dio.options, - 'dating-agency-chat-audio/user/get/friend-footprint-info', - queryParameters: queryParameters, - data: _data, - ) - .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)), - ); + final _options = + _setStreamType>>( + Options(method: 'GET', headers: _headers, extra: _extra) + .compose( + _dio.options, + 'dating-agency-chat-audio/user/get/friend-footprint-info', + queryParameters: queryParameters, + data: _data, + ) + .copyWith( + baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl), + ), + ); final _result = await _dio.fetch>(_options); - late BaseResponse> _value; + late BaseResponse _value; try { - _value = BaseResponse>.fromJson( + _value = BaseResponse.fromJson( _result.data!, - (json) => json is List - ? json.map((i) => i as String).toList() - : List.empty(), + (json) => + FriendFootprintInfoData.fromJson(json as Map), ); } on Object catch (e, s) { errorLogger?.logError(e, s, _options); @@ -505,7 +507,7 @@ class _HomeApi implements HomeApi { Options(method: 'GET', headers: _headers, extra: _extra) .compose( _dio.options, - '/dating-agency-chat-audio/user/page/friend-footprint', + 'dating-agency-chat-audio/user/page/friend-footprint', queryParameters: queryParameters, data: _data, ) diff --git a/lib/network/user_api.dart b/lib/network/user_api.dart index 5e00445..2c1c895 100644 --- a/lib/network/user_api.dart +++ b/lib/network/user_api.dart @@ -1,5 +1,6 @@ import 'package:dating_touchme_app/model/common/oss_data.dart'; import 'package:dating_touchme_app/model/discover/task_data.dart'; +import 'package:dating_touchme_app/model/home/check_black_data.dart'; import 'package:dating_touchme_app/model/home/post_data.dart'; import 'package:dating_touchme_app/model/home/user_info_data.dart'; import 'package:dating_touchme_app/model/mine/bank_card_data.dart'; @@ -335,4 +336,9 @@ abstract class UserApi { Future>> userCreateUserFeedback( @Body() Map data, ); + + @GET(ApiUrls.userCheckUserBlacklist) + Future>> userCheckUserBlacklist({ + @Query('targetUserId') required String targetUserId, + }); } diff --git a/lib/network/user_api.g.dart b/lib/network/user_api.g.dart index 323ce70..8b95397 100644 --- a/lib/network/user_api.g.dart +++ b/lib/network/user_api.g.dart @@ -2035,6 +2035,39 @@ class _UserApi implements UserApi { return httpResponse; } + @override + Future>> userCheckUserBlacklist({ + required String targetUserId, + }) async { + final _extra = {}; + final queryParameters = {r'targetUserId': targetUserId}; + final _headers = {}; + const Map? _data = null; + final _options = _setStreamType>>( + Options(method: 'GET', headers: _headers, extra: _extra) + .compose( + _dio.options, + 'dating-agency-chat-audio/user/check/user-blacklist', + queryParameters: queryParameters, + data: _data, + ) + .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)), + ); + final _result = await _dio.fetch>(_options); + late BaseResponse _value; + try { + _value = BaseResponse.fromJson( + _result.data!, + (json) => CheckBlackData.fromJson(json as Map), + ); + } on Object catch (e, s) { + errorLogger?.logError(e, s, _options); + rethrow; + } + final httpResponse = HttpResponse(_value, _result); + return httpResponse; + } + RequestOptions _setStreamType(RequestOptions requestOptions) { if (T != dynamic && !(requestOptions.responseType == ResponseType.bytes || diff --git a/lib/pages/home/content_card.dart b/lib/pages/home/content_card.dart index 661cad4..a19a857 100644 --- a/lib/pages/home/content_card.dart +++ b/lib/pages/home/content_card.dart @@ -15,7 +15,8 @@ import 'package:get/get.dart'; // 通用头部组件:头像/昵称/在线/认证/Hi/直播中徽标 class _CardHeader extends StatelessWidget { final MarriageData item; - const _CardHeader({required this.item}); + final bool isFoot; + _CardHeader({required this.item, this.isFoot = false}); @override Widget build(BuildContext context) { @@ -123,6 +124,32 @@ class _CardHeader extends StatelessWidget { ], ), ), + if (isFoot) + Container( + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 2, + ), + decoration: BoxDecoration( + color: Color.fromRGBO(237, 237, 237, 1), + borderRadius: BorderRadius.circular(12), + ), + constraints: BoxConstraints( + minWidth: 40, // 设置最小宽度以保证视觉效果 + ), + child: Row( + mainAxisSize: MainAxisSize.min, // 确保Row只占用必要的宽度 + children: [ + const Text( + '相遇过', + style: TextStyle( + fontSize: 9, + color: Color.fromRGBO(166, 166, 166, 1), + ), + ), + ], + ), + ), // 直播状态下显示视频相亲中标签 if (false) Container( @@ -237,7 +264,8 @@ class _CardHeader extends StatelessWidget { // 统一的内容卡片组件,根据接口数据显示不同状态 class ContentCard extends StatelessWidget { final MarriageData item; - ContentCard({required this.item}); + final bool isFoot; + ContentCard({required this.item, this.isFoot = false}); @override Widget build(BuildContext context) { @@ -270,7 +298,7 @@ class ContentCard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ // 头部:头像、昵称、在线状态、按钮等 - 所有按钮都放在HI位置 - _CardHeader(item: item), + _CardHeader(item: item, isFoot: isFoot,), // 内容区域 - 根据类型显示不同内容 _buildContent(), diff --git a/lib/pages/home/friend_footprint_page.dart b/lib/pages/home/friend_footprint_page.dart new file mode 100644 index 0000000..fd22674 --- /dev/null +++ b/lib/pages/home/friend_footprint_page.dart @@ -0,0 +1,153 @@ +import 'package:dating_touchme_app/components/page_appbar.dart'; +import 'package:dating_touchme_app/controller/home/home_controller.dart'; +import 'package:dating_touchme_app/pages/home/content_card.dart'; +import 'package:easy_refresh/easy_refresh.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class FriendFootprintPage extends StatefulWidget { + const FriendFootprintPage({super.key}); + + @override + State createState() => _FriendFootprintPageState(); +} + +class _FriendFootprintPageState extends State { + final HomeController controller = Get.find(); + late final EasyRefreshController _refreshController; + + final ScrollController _scrollController = ScrollController(); + + @override + void initState() { + super.initState(); + _refreshController = EasyRefreshController(controlFinishRefresh: true, controlFinishLoad: true); + controller.loadFriendFootInitialData(); + + } + + @override + void dispose() { + _refreshController.dispose(); + super.dispose(); + } + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PageAppbar(title: "好友足迹"), + body: Obx(() { + if (controller.recommendIsLoading.value && controller.recommendFeed.isEmpty) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CircularProgressIndicator(), + SizedBox(height: 16), + Text('加载数据中...'), + ], + ), + ); + } + return EasyRefresh( + controller: _refreshController, + header: const ClassicHeader( + dragText: '下拉刷新', + armedText: '释放刷新', + readyText: '刷新中...', + processingText: '刷新中...', + processedText: '刷新完成', + failedText: '刷新失败', + noMoreText: '没有更多数据', + showMessage: false + ), + footer: ClassicFooter( + dragText: '上拉加载', + armedText: '释放加载', + readyText: '加载中...', + processingText: '加载中...', + processedText: '加载完成', + failedText: '加载失败', + noMoreText: '没有更多数据', + showMessage: false + ), + // 下拉刷新 + onRefresh: () async { + print('推荐列表下拉刷新被触发'); + try { + await controller.refreshFriendData(); + print( '推荐列表刷新完成, hasMore: $controller.recommendHasMore.value'); + _refreshController.finishRefresh(); + _refreshController.resetFooter(); + } catch (e) { + print('推荐列表刷新失败: $e'); + _refreshController.finishRefresh(IndicatorResult.fail); + } + }, + // 上拉加载更多 + onLoad: () async { + print('推荐列表上拉加载被触发, hasMore: $controller.recommendHasMore.value'); + try { + await controller.loadFriendFootMoreData(); + // 完成加载,根据是否有更多数据决定 + if (controller.friendHasMore.value) { + _refreshController.finishLoad(IndicatorResult.success); + print('推荐列表加载更多成功'); + } else { + _refreshController.finishLoad(IndicatorResult.noMore); + print('推荐列表没有更多数据了'); + } + } catch (e) { + print('推荐列表加载更多失败: $e'); + _refreshController.finishLoad(IndicatorResult.fail); + } + }, + child: ListView.separated( + controller: _scrollController, + // 关键:始终允许滚动,即使内容不足 + // 移除顶部 padding,让刷新指示器可以正确显示在 AppBar 下方 + padding: EdgeInsets.only(left: 12, right: 12), + itemBuilder: (context, index) { + // 空数据状态 + if (controller.friendFootFeed.isEmpty && index == 0) { + // 使用足够的高度确保可以滚动 + if (controller.friendIsLoading.value) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CircularProgressIndicator(), + SizedBox(height: 16), + Text('加载数据中...'), + ], + ), + ); + } else { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('暂无数据'), + ], + ), + ); + } + } + // 数据项 + final item = controller.friendFootFeed[index]; + return ContentCard(item: item, isFoot: true,); + }, + separatorBuilder: (context, index) { + // 空状态或加载状态时不显示分隔符 + if (controller.friendFootFeed.isEmpty) { + return const SizedBox.shrink(); + } + return const SizedBox(height: 12); + }, + // 至少显示一个 item(用于显示加载或空状态) + itemCount: controller.friendFootFeed.isEmpty ? 1 : controller.friendFootFeed.length, + ) + ); + }), + ); + } +} diff --git a/lib/pages/home/recommend_tab.dart b/lib/pages/home/recommend_tab.dart index d6af006..1dbbb21 100644 --- a/lib/pages/home/recommend_tab.dart +++ b/lib/pages/home/recommend_tab.dart @@ -1,10 +1,15 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:dating_touchme_app/extension/ex_widget.dart'; import 'package:dating_touchme_app/generated/assets.dart'; +import 'package:dating_touchme_app/pages/home/friend_footprint_page.dart'; import 'package:easy_refresh/easy_refresh.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'package:dating_touchme_app/controller/home/home_controller.dart'; import 'package:dating_touchme_app/pages/home/content_card.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; +import 'package:flutter_swiper_null_safety/flutter_swiper_null_safety.dart'; /// 推荐列表 Tab class RecommendTab extends StatefulWidget { @@ -173,60 +178,79 @@ class _RecommendTabState extends State ) ); }), - Positioned( - right: 0, - bottom: 42, - child: Obx(() { - return AnimatedContainer( - duration: const Duration(milliseconds: 250), - width: _shrink ? 50 : 142, - height: 50, - padding: EdgeInsets.all(5), - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(50)), - gradient: LinearGradient( - begin: Alignment.centerLeft, - end: Alignment.centerRight, - colors: [ - Color(0xFF8359FF), - Color(0xFF3D8AE0), - ], + Obx(() { + if(controller.friendFootprintInfo.isNotEmpty) { + return Positioned( + right: 0, + bottom: 42, + child: AnimatedContainer( + duration: const Duration(milliseconds: 250), + width: _shrink ? 50 : 142, + height: 50, + padding: EdgeInsets.all(5), + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(50)), + gradient: LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + colors: [ + Color(0xFF8359FF), + Color(0xFF3D8AE0), + ], + ), ), - ), - child: Row( - children: [ - ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(40)), - child: Image.asset( - Assets.imagesUserAvatar, + child: Row( + children: [ + SizedBox( width: 40, height: 40, - ), - ), - if(!_shrink) SizedBox(width: 4,), - if(!_shrink) Column( - children: [ - Text( - "与你匹配的", - style: TextStyle( - fontSize: 15, - color: Colors.white - ), + child: Swiper( + autoplay: true, + itemCount: controller.friendFootprintInfo.length, + loop: true, + itemBuilder: (BuildContext context, int index) { + return ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(40)), + child: CachedNetworkImage( + imageUrl: controller.friendFootprintInfo[index], + width: 40, + height: 40, + fit: BoxFit.cover, + ), + ); + }, ), - Text( - "${controller.cityCode.value ?? 0}人正在连麦...", - style: TextStyle( - fontSize: 11, - color: Colors.white + ), + + if(!_shrink) SizedBox(width: 4,), + if(!_shrink) Column( + children: [ + Text( + "与你匹配的", + style: TextStyle( + fontSize: 15, + color: Colors.white + ), ), - ) - ], - ) - ], - ), + Text( + "${controller.friendFootprintInfo.length}人正在连麦...", + style: TextStyle( + fontSize: 11, + color: Colors.white + ), + ) + ], + ) + ], + ), + ).onTap((){ + Get.to(() => FriendFootprintPage()); + }), ); - }), - ) + } else { + return SizedBox(); + } + }) ], ); } diff --git a/lib/pages/mine/mine_page.dart b/lib/pages/mine/mine_page.dart index 29c8998..a95398f 100644 --- a/lib/pages/mine/mine_page.dart +++ b/lib/pages/mine/mine_page.dart @@ -394,18 +394,6 @@ class MinePageState extends State with AutomaticKeepAliveClientMixin{ Get.to(() => SettingPage()); } ), - TDCell(arrow: true, - leftIconWidget: Image.asset( - gaplessPlayback: true,Assets.imagesCustomer, height: 22.w, width: 22.w), - titleWidget: Text( - "联系客服", - style: TextStyle( - fontSize: 14.w - ), - ), onClick: (cell) { - Get.to(() => FeedbackPage()); - } - ), TDCell(arrow: true, leftIconWidget: Image.asset( gaplessPlayback: true,Assets.imagesMail, height: 22.w, width: 22.w), @@ -415,9 +403,21 @@ class MinePageState extends State with AutomaticKeepAliveClientMixin{ fontSize: 14.w ), ), onClick: (cell) { - Get.to(() => UserHelpCenterPage()); + Get.to(() => FeedbackPage()); } ), + // TDCell(arrow: true, + // leftIconWidget: Image.asset( + // gaplessPlayback: true,Assets.imagesCustomer, height: 22.w, width: 22.w), + // titleWidget: Text( + // "意见反馈", + // style: TextStyle( + // fontSize: 14.w + // ), + // ), onClick: (cell) { + // Get.to(() => UserHelpCenterPage()); + // } + // ), ], ), ], diff --git a/lib/pages/mine/real_feedback_page.dart b/lib/pages/mine/real_feedback_page.dart index 68978ba..c88bba8 100644 --- a/lib/pages/mine/real_feedback_page.dart +++ b/lib/pages/mine/real_feedback_page.dart @@ -264,7 +264,7 @@ class RealFeedbackPage extends StatelessWidget { vertical: 0, horizontal: 0 ), - hintText: "请输入交友心声", + hintText: "请输入您的宝贵的建议", border: const OutlineInputBorder( borderSide: BorderSide.none, // 这将移除边框 // 可选:设置圆角 diff --git a/lib/pages/setting/blacklist_page.dart b/lib/pages/setting/blacklist_page.dart index 656e895..f3a51de 100644 --- a/lib/pages/setting/blacklist_page.dart +++ b/lib/pages/setting/blacklist_page.dart @@ -137,7 +137,7 @@ class _BlackItemState extends State { ), ), Text( - "30岁·广州", + "拉黑时间:${widget.item.createTime}", style: TextStyle( fontSize: 11.w, color: const Color.fromRGBO(144, 144, 144, 1) @@ -199,7 +199,7 @@ class _BlackItemState extends State { ), ), ).onTap((){ - widget.controller.unBlack(widget.item.blackUserId ?? ""); + widget.controller.unBlack(widget.item.id ?? ""); }), Container( height: 65.w,