8 changed files with 511 additions and 35 deletions
Split View
Diff Options
-
151lib/controller/discover/visitor_controller.dart
-
25lib/controller/mine/auth_controller.dart
-
8lib/controller/mine/mine_controller.dart
-
75lib/model/discover/visitor_model.dart
-
258lib/pages/discover/visitor_list_page.dart
-
14lib/pages/mine/auth_center_page.dart
-
7lib/pages/mine/mine_page.dart
-
8lib/pages/mine/real_name_page.dart
@ -0,0 +1,151 @@ |
|||
// controllers/visitor_controller.dart |
|||
import 'package:get/get.dart'; |
|||
import 'package:pull_to_refresh/pull_to_refresh.dart'; |
|||
import '../../model/discover/visitor_model.dart'; |
|||
|
|||
class VisitorController extends GetxController { |
|||
// 访客列表 |
|||
var visitors = <VisitorModel>[].obs; |
|||
|
|||
// Refresh controllers |
|||
final RefreshController refreshController = RefreshController(); |
|||
final RefreshController loadMoreController = RefreshController(); |
|||
|
|||
// 分页参数 |
|||
var currentPage = 1.obs; |
|||
final int pageSize = 15; |
|||
var hasMore = true.obs; |
|||
var isLoading = false.obs; |
|||
|
|||
@override |
|||
void onInit() { |
|||
super.onInit(); |
|||
_loadInitialData(); |
|||
} |
|||
|
|||
// 加载初始数据 |
|||
void _loadInitialData() async { |
|||
isLoading.value = true; |
|||
await Future.delayed(Duration(milliseconds: 800)); // 模拟网络延迟 |
|||
|
|||
final initialVisitors = _generateMockData(1, pageSize); |
|||
visitors.assignAll(initialVisitors); |
|||
|
|||
isLoading.value = false; |
|||
hasMore.value = initialVisitors.length == pageSize; |
|||
} |
|||
|
|||
// 下拉刷新 |
|||
void onRefresh() async { |
|||
currentPage.value = 1; |
|||
hasMore.value = true; |
|||
|
|||
await Future.delayed(Duration(milliseconds: 1000)); // 模拟网络延迟 |
|||
|
|||
final newVisitors = _generateMockData(1, pageSize); |
|||
visitors.assignAll(newVisitors); |
|||
|
|||
refreshController.refreshCompleted(); |
|||
hasMore.value = newVisitors.length == pageSize; |
|||
|
|||
if (newVisitors.isEmpty) { |
|||
refreshController.loadNoData(); |
|||
} else { |
|||
refreshController.loadComplete(); |
|||
} |
|||
} |
|||
|
|||
// 上拉加载更多 |
|||
void onLoadMore() async { |
|||
if (!hasMore.value) { |
|||
loadMoreController.loadNoData(); |
|||
return; |
|||
} |
|||
|
|||
currentPage.value++; |
|||
|
|||
await Future.delayed(Duration(milliseconds: 800)); // 模拟网络延迟 |
|||
|
|||
final newVisitors = _generateMockData(currentPage.value, pageSize); |
|||
|
|||
if (newVisitors.isEmpty) { |
|||
hasMore.value = false; |
|||
loadMoreController.loadNoData(); |
|||
} else { |
|||
visitors.addAll(newVisitors); |
|||
loadMoreController.loadComplete(); |
|||
hasMore.value = false; |
|||
} |
|||
} |
|||
|
|||
// 删除访客 |
|||
void deleteVisitor(String visitorId) { |
|||
// visitors.removeWhere((visitor) => visitor.id == visitorId); |
|||
Get.snackbar('成功', '已删除访客记录'); |
|||
} |
|||
|
|||
// 清空所有访客记录 |
|||
void clearAllVisitors() { |
|||
visitors.clear(); |
|||
Get.snackbar('成功', '已清空所有访客记录'); |
|||
} |
|||
|
|||
// 获取今日访客数量 |
|||
int get todayVisitorsCount { |
|||
return visitors.length; |
|||
} |
|||
|
|||
// 获取在线访客数量 |
|||
int get onlineVisitorsCount { |
|||
return visitors.length; |
|||
} |
|||
|
|||
// 模拟数据生成 |
|||
List<VisitorModel> _generateMockData(int page, int size) { |
|||
if (page > 3) return []; // 模拟只有3页数据 |
|||
|
|||
final names = [ |
|||
'张三', '李四', '王五', '赵六', '钱七', '孙八', '周九', '吴十', |
|||
'小明', '小红', '小刚', '小花', '大山', '小云', '星辰', '大海' |
|||
]; |
|||
|
|||
final avatars = [ |
|||
'https://randomuser.me/api/portraits/men/1.jpg', |
|||
'https://randomuser.me/api/portraits/women/1.jpg', |
|||
'https://randomuser.me/api/portraits/men/2.jpg', |
|||
'https://randomuser.me/api/portraits/women/2.jpg', |
|||
'https://randomuser.me/api/portraits/men/3.jpg', |
|||
'https://randomuser.me/api/portraits/women/3.jpg', |
|||
'https://randomuser.me/api/portraits/men/4.jpg', |
|||
'https://randomuser.me/api/portraits/women/4.jpg', |
|||
]; |
|||
|
|||
final pages = [ |
|||
'首页', '产品页', '关于我们', '博客', '联系方式', '服务页', '价格页', '案例页' |
|||
]; |
|||
|
|||
final startIndex = (page - 1) * size; |
|||
final endIndex = startIndex + size; |
|||
|
|||
return List.generate(size, (index) { |
|||
final globalIndex = startIndex + index; |
|||
final isOnline = globalIndex % 3 == 0; // 每3个有一个在线 |
|||
|
|||
return VisitorModel( |
|||
miId: 'visitor_${globalIndex + 1}', |
|||
nickName: names[globalIndex % names.length], |
|||
profilePhoto: avatars[globalIndex % avatars.length], |
|||
visitTime: '2025-11-14 10:42:42', |
|||
onlineStatus: 2, |
|||
describeInfo: '阿萨德垃圾袋杰克伦敦撒娇克拉斯的健康拉屎的 金坷垃四大皆空拉屎的距离考试啊' |
|||
); |
|||
}); |
|||
} |
|||
|
|||
@override |
|||
void onClose() { |
|||
refreshController.dispose(); |
|||
loadMoreController.dispose(); |
|||
super.onClose(); |
|||
} |
|||
} |
|||
@ -0,0 +1,75 @@ |
|||
class VisitorModel { |
|||
final String? miId; |
|||
final String? profilePhoto; |
|||
final String? nickName; |
|||
final int? height; |
|||
final String? visitTime; |
|||
final String? education; |
|||
final int? age; |
|||
final int? minimumIncome; |
|||
final int? maximumIncome; |
|||
final String? income; |
|||
final String? describeInfo; |
|||
final int? vip; |
|||
final int? miSessionType; |
|||
final int? genderCode; |
|||
final int? onlineStatus; |
|||
|
|||
VisitorModel({ |
|||
this.miId, |
|||
this.profilePhoto, |
|||
this.nickName, |
|||
this.height, |
|||
this.visitTime, |
|||
this.education, |
|||
this.age, |
|||
this.minimumIncome, |
|||
this.maximumIncome, |
|||
this.income, |
|||
this.describeInfo, |
|||
this.vip, |
|||
this.miSessionType, |
|||
this.genderCode, |
|||
this.onlineStatus, |
|||
}); |
|||
|
|||
factory VisitorModel.fromJson(Map<String, dynamic> json) { |
|||
return VisitorModel( |
|||
miId: json['miId'] as String?, |
|||
profilePhoto: json['profilePhoto'] as String?, |
|||
nickName: json['nickName'] as String?, |
|||
height: json['height'] as int?, |
|||
visitTime: json['visitTime'] as String?, |
|||
education: json['education'] as String?, |
|||
age: json['age'] as int?, |
|||
minimumIncome: json['minimumIncome'] as int?, |
|||
maximumIncome: json['maximumIncome'] as int?, |
|||
income: json['income'] as String?, |
|||
describeInfo: json['describeInfo'] as String?, |
|||
vip: json['vip'] as int?, |
|||
miSessionType: json['miSessionType'] as int?, |
|||
genderCode: json['genderCode'] as int?, |
|||
onlineStatus: json['onlineStatus'] as int?, |
|||
); |
|||
} |
|||
|
|||
Map<String, dynamic> toJson() { |
|||
return { |
|||
'miId': miId, |
|||
'profilePhoto': profilePhoto, |
|||
'nickName': nickName, |
|||
'height': height, |
|||
'visitTime': visitTime, |
|||
'education': education, |
|||
'age': age, |
|||
'minimumIncome': minimumIncome, |
|||
'maximumIncome': maximumIncome, |
|||
'income': income, |
|||
'describeInfo': describeInfo, |
|||
'vip': vip, |
|||
'miSessionType': miSessionType, |
|||
'genderCode': genderCode, |
|||
'onlineStatus': onlineStatus, |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1,258 @@ |
|||
// pages/visitor_list_page.dart |
|||
import 'package:cached_network_image/cached_network_image.dart'; |
|||
import 'package:dating_touchme_app/extension/ex_widget.dart'; |
|||
import 'package:flutter/material.dart'; |
|||
import 'package:get/get.dart'; |
|||
import 'package:pull_to_refresh/pull_to_refresh.dart'; |
|||
|
|||
import '../../controller/discover/visitor_controller.dart'; |
|||
import '../../model/discover/visitor_model.dart'; |
|||
|
|||
class VisitorListPage extends StatelessWidget { |
|||
final VisitorController visitorController = Get.put(VisitorController()); |
|||
|
|||
VisitorListPage({Key? key}) : super(key: key); |
|||
|
|||
@override |
|||
Widget build(BuildContext context) { |
|||
return Scaffold( |
|||
backgroundColor: Color(0xffF5F5F5), |
|||
appBar: AppBar( |
|||
title: Obx(() => Text( |
|||
'最近访客 (${visitorController.todayVisitorsCount})', |
|||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), |
|||
)), |
|||
centerTitle: true, |
|||
backgroundColor: Colors.white, |
|||
actions: [ |
|||
// 更多操作 |
|||
PopupMenuButton<String>( |
|||
onSelected: (value) { |
|||
if (value == 'clear') { |
|||
_showClearDialog(); |
|||
} else if (value == 'sort') { |
|||
_showSortDialog(); |
|||
} |
|||
}, |
|||
itemBuilder: (BuildContext context) => [ |
|||
PopupMenuItem(value: 'sort', child: Text('排序方式')), |
|||
PopupMenuItem(value: 'clear', child: Text('清空记录')), |
|||
], |
|||
), |
|||
], |
|||
), |
|||
body: Obx(() { |
|||
if (visitorController.isLoading.value && visitorController.visitors.isEmpty) { |
|||
return Center( |
|||
child: Column( |
|||
mainAxisAlignment: MainAxisAlignment.center, |
|||
children: [ |
|||
CircularProgressIndicator(), |
|||
SizedBox(height: 16), |
|||
Text('加载访客数据中...'), |
|||
], |
|||
), |
|||
); |
|||
} |
|||
|
|||
if (visitorController.visitors.isEmpty) { |
|||
return Center( |
|||
child: Column( |
|||
mainAxisAlignment: MainAxisAlignment.center, |
|||
children: [ |
|||
Icon(Icons.people_outline, size: 80, color: Colors.grey[300]), |
|||
SizedBox(height: 16), |
|||
Text( |
|||
'暂无访客记录', |
|||
style: TextStyle(fontSize: 18, color: Colors.grey), |
|||
), |
|||
SizedBox(height: 8), |
|||
Text( |
|||
'下拉刷新获取数据', |
|||
style: TextStyle(color: Colors.grey), |
|||
), |
|||
], |
|||
), |
|||
); |
|||
} |
|||
|
|||
return SmartRefresher( |
|||
controller: visitorController.refreshController, |
|||
enablePullDown: true, |
|||
enablePullUp: visitorController.hasMore.value, |
|||
onRefresh: visitorController.onRefresh, |
|||
onLoading: visitorController.onLoadMore, |
|||
header: ClassicHeader( |
|||
idleText: '下拉刷新', |
|||
releaseText: '松开刷新', |
|||
refreshingText: '刷新中...', |
|||
completeText: '刷新完成', |
|||
failedText: '刷新失败', |
|||
height: 60, |
|||
), |
|||
footer: CustomFooter( |
|||
builder: (BuildContext context, LoadStatus? mode) { |
|||
Widget body; |
|||
if (mode == LoadStatus.idle) { |
|||
body = Text("上拉加载更多"); |
|||
} else if (mode == LoadStatus.loading) { |
|||
body = CircularProgressIndicator(); |
|||
} else if (mode == LoadStatus.failed) { |
|||
body = Text("加载失败,点击重试"); |
|||
} else if (mode == LoadStatus.canLoading) { |
|||
body = Text("松开加载更多"); |
|||
} else { |
|||
body = Text("没有更多数据了"); |
|||
} |
|||
return SizedBox( |
|||
height: 36, |
|||
child: Center(child: body), |
|||
); |
|||
}, |
|||
), |
|||
child: ListView.builder( |
|||
padding: const EdgeInsets.only(top: 8, right: 10, left: 10), |
|||
itemCount: visitorController.visitors.length, |
|||
itemBuilder: (context, index) { |
|||
final visitor = visitorController.visitors[index]; |
|||
return VisitorListItem(visitor: visitor); |
|||
}, |
|||
), |
|||
); |
|||
}), |
|||
); |
|||
} |
|||
|
|||
void _showClearDialog() { |
|||
Get.dialog( |
|||
AlertDialog( |
|||
title: Text('确认清空'), |
|||
content: Text('确定要清空所有访客记录吗?此操作不可恢复。'), |
|||
actions: [ |
|||
TextButton( |
|||
onPressed: () => Get.back(), |
|||
child: Text('取消'), |
|||
), |
|||
TextButton( |
|||
onPressed: () { |
|||
visitorController.clearAllVisitors(); |
|||
Get.back(); |
|||
}, |
|||
child: Text('确定', style: TextStyle(color: Colors.red)), |
|||
), |
|||
], |
|||
), |
|||
); |
|||
} |
|||
|
|||
void _showSortDialog() { |
|||
Get.dialog( |
|||
AlertDialog( |
|||
title: Text('排序方式'), |
|||
content: Column( |
|||
mainAxisSize: MainAxisSize.min, |
|||
children: [ |
|||
ListTile( |
|||
title: Text('按访问时间'), |
|||
onTap: () { |
|||
// 实现排序逻辑 |
|||
Get.back(); |
|||
}, |
|||
), |
|||
ListTile( |
|||
title: Text('按访问时长'), |
|||
onTap: () { |
|||
// 实现排序逻辑 |
|||
Get.back(); |
|||
}, |
|||
), |
|||
ListTile( |
|||
title: Text('按访问次数'), |
|||
onTap: () { |
|||
// 实现排序逻辑 |
|||
Get.back(); |
|||
}, |
|||
), |
|||
], |
|||
), |
|||
), |
|||
); |
|||
} |
|||
} |
|||
|
|||
class VisitorListItem extends StatelessWidget { |
|||
final VisitorModel visitor; |
|||
|
|||
const VisitorListItem({Key? key, required this.visitor}) : super(key: key); |
|||
|
|||
@override |
|||
Widget build(BuildContext context) { |
|||
return Container( |
|||
decoration: BoxDecoration( |
|||
color: Colors.white, |
|||
borderRadius: BorderRadius.circular(12), |
|||
), |
|||
padding: const EdgeInsets.all(12), |
|||
margin: EdgeInsets.only(bottom: 8), |
|||
child: Row( |
|||
crossAxisAlignment: CrossAxisAlignment.center, |
|||
children: [ |
|||
// 左侧图片 |
|||
Container( |
|||
width: 50, |
|||
height: 50, |
|||
decoration: BoxDecoration( |
|||
borderRadius: BorderRadius.circular(8), |
|||
color: Colors.blue[100], |
|||
image: DecorationImage( |
|||
image: NetworkImage(visitor.profilePhoto!), |
|||
fit: BoxFit.cover, |
|||
), |
|||
), |
|||
), |
|||
SizedBox(width: 12), |
|||
Expanded( |
|||
child: Column( |
|||
children: [ |
|||
Row( |
|||
children: [ |
|||
Text( |
|||
visitor.nickName!, |
|||
style: TextStyle( |
|||
fontSize: 14, |
|||
), |
|||
), |
|||
Spacer(), |
|||
Text( |
|||
visitor.visitTime!, |
|||
style: TextStyle( |
|||
color: Colors.grey[500], |
|||
fontSize: 13, |
|||
), |
|||
), |
|||
], |
|||
), |
|||
SizedBox(height: 4), |
|||
SizedBox( |
|||
// height: 20, // 固定高度20 |
|||
child: Text( |
|||
visitor.describeInfo!, |
|||
style: TextStyle( |
|||
fontSize: 13, |
|||
color: Color.fromRGBO(51, 51, 51, 0.6), |
|||
), |
|||
overflow: TextOverflow.ellipsis, // 文本超出显示... |
|||
maxLines: 1, // 限制为单行 |
|||
), |
|||
), |
|||
], |
|||
) |
|||
) |
|||
], |
|||
), |
|||
).onTap((){ |
|||
// _showVisitorDetail(visitor); |
|||
}); |
|||
} |
|||
|
|||
} |
|||
Write
Preview
Loading…
Cancel
Save