1、聚合搜索
2、公共视频详情页
This commit is contained in:
parent
0b99562766
commit
ce0331e4c6
@ -14,5 +14,8 @@ class CommonApi {
|
|||||||
// 获取字典枚举
|
// 获取字典枚举
|
||||||
static const String dictionaryApi = '/app/sys/dict/type/';
|
static const String dictionaryApi = '/app/sys/dict/type/';
|
||||||
|
|
||||||
|
// 聚合搜索
|
||||||
|
static const String aggregationSearchApi = '/app/common/search';
|
||||||
|
|
||||||
///resource/oss/upload
|
///resource/oss/upload
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,11 @@ class VideoApi {
|
|||||||
// post
|
// post
|
||||||
static const String myPublicList = '/app/vlog/myPublicList'; // 我发布的视频
|
static const String myPublicList = '/app/vlog/myPublicList'; // 我发布的视频
|
||||||
static const String myLikedList = '/app/vlog/myLikedList'; // 我点赞的视频
|
static const String myLikedList = '/app/vlog/myLikedList'; // 我点赞的视频
|
||||||
static const String videoCommentList = '/app/comment/list'; // 视频评论列表
|
static const String videoCommentList = '/app/comment/page'; // 视频评论列表
|
||||||
static const String doVideoComment = '/app/comment/publish'; // 发布评论
|
static const String doVideoComment = '/app/comment/publish'; // 发布评论
|
||||||
static const String reportVideoApi = '/app/feedback/add'; // 投诉视频
|
static const String reportVideoApi = '/app/feedback/add'; // 投诉视频
|
||||||
|
static const String videoDetailApi = '/app/vlog/detail/'; // 根据视频Id获取视频系详情
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -117,6 +117,7 @@ class ShopIndexController extends GetxController with GetSingleTickerProviderSta
|
|||||||
});
|
});
|
||||||
|
|
||||||
final data = res['data']['records'];
|
final data = res['data']['records'];
|
||||||
|
print('商品返回数据------------------------->${data}');
|
||||||
tab.dataList.addAll(data);
|
tab.dataList.addAll(data);
|
||||||
// logger.w(res);
|
// logger.w(res);
|
||||||
|
|
||||||
|
@ -597,7 +597,8 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
|
|||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// 这里带上分享人的ID
|
// 这里带上分享人的ID
|
||||||
Get.toNamed('/goods');
|
// Get.toNamed('/goods');
|
||||||
|
Get.toNamed('/goods', arguments: {});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
|
@ -48,13 +48,13 @@ class _GoodsState extends State<Goods> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
final shopId = Get.arguments;
|
final goodsId = Get.arguments['goodsId'];
|
||||||
scrollController.addListener(() {
|
scrollController.addListener(() {
|
||||||
setState(() {
|
setState(() {
|
||||||
scrollOffset = scrollController.offset;
|
scrollOffset = scrollController.offset;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
shopDetail(shopId);
|
shopDetail(goodsId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -64,9 +64,9 @@ class _GoodsState extends State<Goods> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
///商品详情
|
///商品详情
|
||||||
void shopDetail(shopId) async {
|
void shopDetail(goodsId) async {
|
||||||
try {
|
try {
|
||||||
final res = await Http.get('${ShopApi.shopDetail}/$shopId');
|
final res = await Http.get('${ShopApi.shopDetail}/$goodsId');
|
||||||
logger.e(res['data']);
|
logger.e(res['data']);
|
||||||
setState(() {
|
setState(() {
|
||||||
shopObj = res['data']; // 注意取 data 部分
|
shopObj = res['data']; // 注意取 data 部分
|
||||||
|
@ -104,7 +104,8 @@ class _IndexPageState extends State<IndexPage> with SingleTickerProviderStateMix
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Get.toNamed('/goods', arguments: item['id']);
|
// Get.toNamed('/goods', arguments: item['id']);
|
||||||
|
Get.toNamed('/goods', arguments: {'goodsId': item['id']});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:loopin/IM/controller/chat_controller.dart';
|
||||||
|
import 'package:loopin/IM/im_core.dart';
|
||||||
|
import 'package:loopin/IM/im_message.dart';
|
||||||
|
import 'package:loopin/IM/im_service.dart' hide logger;
|
||||||
import 'package:loopin/service/http.dart';
|
import 'package:loopin/service/http.dart';
|
||||||
|
import 'package:loopin/api/common_api.dart';
|
||||||
|
import 'package:loopin/utils/index.dart';
|
||||||
import 'package:loopin/components/my_toast.dart';
|
import 'package:loopin/components/my_toast.dart';
|
||||||
import '../../behavior/custom_scroll_behavior.dart';
|
import '../../behavior/custom_scroll_behavior.dart';
|
||||||
|
|
||||||
@ -18,11 +24,24 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
|
|||||||
final TextEditingController _searchController = TextEditingController();
|
final TextEditingController _searchController = TextEditingController();
|
||||||
final FocusNode _searchFocusNode = FocusNode();
|
final FocusNode _searchFocusNode = FocusNode();
|
||||||
|
|
||||||
|
// 分页参数
|
||||||
|
static const int _pageSize = 20;
|
||||||
|
int _videoCurrentPage = 1;
|
||||||
|
int _productCurrentPage = 1;
|
||||||
|
int _userCurrentPage = 1;
|
||||||
|
bool _videoHasMore = true;
|
||||||
|
bool _productHasMore = true;
|
||||||
|
bool _userHasMore = true;
|
||||||
|
|
||||||
// 三个tab的数据
|
// 三个tab的数据
|
||||||
List<dynamic> _videoResults = [];
|
List<dynamic> _videoResults = [];
|
||||||
List<dynamic> _productResults = [];
|
List<dynamic> _productResults = [];
|
||||||
List<dynamic> _userResults = [];
|
List<dynamic> _userResults = [];
|
||||||
bool _isLoading = true;
|
bool _isLoading = true;
|
||||||
|
bool _isLoadingMore = false;
|
||||||
|
|
||||||
|
// 统一的高度常量
|
||||||
|
static const double _itemCornerRadius = 8;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -61,7 +80,7 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
|
|||||||
void _onTabChanged() {
|
void _onTabChanged() {
|
||||||
if (_tabController.indexIsChanging) {
|
if (_tabController.indexIsChanging) {
|
||||||
// 加载对应Tab的数据
|
// 加载对应Tab的数据
|
||||||
_loadTabData(_tabController.index);
|
_loadTabData(_tabController.index, isRefresh: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,6 +103,8 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
|
|||||||
setState(() {
|
setState(() {
|
||||||
_searchQuery = newQuery;
|
_searchQuery = newQuery;
|
||||||
_isLoading = true;
|
_isLoading = true;
|
||||||
|
// 重置分页状态
|
||||||
|
_resetPagination();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 关闭键盘
|
// 关闭键盘
|
||||||
@ -93,6 +114,19 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
|
|||||||
_loadInitialData();
|
_loadInitialData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 重置分页状态
|
||||||
|
void _resetPagination() {
|
||||||
|
_videoCurrentPage = 1;
|
||||||
|
_productCurrentPage = 1;
|
||||||
|
_userCurrentPage = 1;
|
||||||
|
_videoHasMore = true;
|
||||||
|
_productHasMore = true;
|
||||||
|
_userHasMore = true;
|
||||||
|
_videoResults.clear();
|
||||||
|
_productResults.clear();
|
||||||
|
_userResults.clear();
|
||||||
|
}
|
||||||
|
|
||||||
// 加载初始数据
|
// 加载初始数据
|
||||||
Future<void> _loadInitialData() async {
|
Future<void> _loadInitialData() async {
|
||||||
try {
|
try {
|
||||||
@ -100,8 +134,8 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
|
|||||||
_isLoading = true;
|
_isLoading = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 加载第一个Tab的数据
|
// 加载当前Tab的数据
|
||||||
await _loadTabData(_initialTabIndex);
|
await _loadTabData(_tabController.index, isRefresh: true);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('加载数据失败: $e');
|
print('加载数据失败: $e');
|
||||||
MyToast().tip(
|
MyToast().tip(
|
||||||
@ -117,64 +151,210 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 加载指定Tab的数据
|
// 加载指定Tab的数据
|
||||||
Future<void> _loadTabData(int tabIndex) async {
|
Future<void> _loadTabData(int tabIndex, {bool isRefresh = false}) async {
|
||||||
try {
|
try {
|
||||||
|
if (isRefresh) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoading = true;
|
_isLoading = true;
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
setState(() {
|
||||||
|
_isLoadingMore = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
int currentPage;
|
||||||
|
List<dynamic> currentResults;
|
||||||
|
bool hasMore;
|
||||||
|
|
||||||
switch (tabIndex) {
|
switch (tabIndex) {
|
||||||
case 0: // 视频
|
case 0: // 视频
|
||||||
final res = await Http.get('/api/search/videos', params: {
|
currentPage = isRefresh ? 1 : _videoCurrentPage + 1;
|
||||||
'keyword': _searchQuery,
|
currentResults = _videoResults;
|
||||||
'page': 1,
|
hasMore = _videoHasMore;
|
||||||
'current': 20,
|
|
||||||
});
|
|
||||||
if (res['code'] == 200) {
|
|
||||||
setState(() {
|
|
||||||
_videoResults = res['data']['list'] ?? [];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 1: // 商品
|
case 1: // 商品
|
||||||
final res = await Http.get('/api/search/products', params: {
|
currentPage = isRefresh ? 1 : _productCurrentPage + 1;
|
||||||
'keyword': _searchQuery,
|
currentResults = _productResults;
|
||||||
'page': 1,
|
hasMore = _productHasMore;
|
||||||
'current': 20,
|
|
||||||
});
|
|
||||||
if (res['code'] == 200) {
|
|
||||||
setState(() {
|
|
||||||
_productResults = res['data']['list'] ?? [];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 2: // 用户
|
case 2: // 用户
|
||||||
final res = await Http.get('/api/search/users', params: {
|
currentPage = isRefresh ? 1 : _userCurrentPage + 1;
|
||||||
'keyword': _searchQuery,
|
currentResults = _userResults;
|
||||||
'page': 1,
|
hasMore = _userHasMore;
|
||||||
'current': 20,
|
|
||||||
});
|
|
||||||
if (res['code'] == 200) {
|
|
||||||
setState(() {
|
|
||||||
_userResults = res['data']['list'] ?? [];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有更多数据,直接返回
|
||||||
|
if (!hasMore && !isRefresh) {
|
||||||
|
setState(() {
|
||||||
|
_isLoadingMore = false;
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final data = {
|
||||||
|
'title': _searchQuery,
|
||||||
|
'size': _pageSize,
|
||||||
|
'type': tabIndex+1, // 1-视频;2-商品;3-用户
|
||||||
|
'current': currentPage,
|
||||||
|
};
|
||||||
|
final res = await Http.post(CommonApi.aggregationSearchApi, data: data);
|
||||||
|
|
||||||
|
if (res['code'] == 200) {
|
||||||
|
final newData = res['data']['records'] ?? [];
|
||||||
|
final total = res['data']['total'] ?? 0;
|
||||||
|
print('搜索数据结果$newData');
|
||||||
|
print('搜索数据参数$data');
|
||||||
|
setState(() {
|
||||||
|
switch (tabIndex) {
|
||||||
|
case 0:
|
||||||
|
if (isRefresh) {
|
||||||
|
_videoResults = newData;
|
||||||
|
_videoCurrentPage = 1;
|
||||||
|
} else {
|
||||||
|
_videoResults.addAll(newData);
|
||||||
|
_videoCurrentPage = currentPage;
|
||||||
|
}
|
||||||
|
_videoHasMore = _videoResults.length < total;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if (isRefresh) {
|
||||||
|
_productResults = newData;
|
||||||
|
_productCurrentPage = 1;
|
||||||
|
} else {
|
||||||
|
_productResults.addAll(newData);
|
||||||
|
_productCurrentPage = currentPage;
|
||||||
|
}
|
||||||
|
_productHasMore = _productResults.length < total;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (isRefresh) {
|
||||||
|
_userResults = newData;
|
||||||
|
_userCurrentPage = 1;
|
||||||
|
} else {
|
||||||
|
_userResults.addAll(newData);
|
||||||
|
_userCurrentPage = currentPage;
|
||||||
|
}
|
||||||
|
_userHasMore = _userResults.length < total;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('加载Tab数据失败: $e');
|
print('加载Tab数据失败: $e');
|
||||||
|
if (!isRefresh) {
|
||||||
MyToast().tip(
|
MyToast().tip(
|
||||||
title: '加载失败',
|
title: '加载更多失败',
|
||||||
position: 'center',
|
position: 'center',
|
||||||
type: 'error',
|
type: 'error',
|
||||||
);
|
);
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
if (isRefresh) {
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
|
} else {
|
||||||
|
_isLoadingMore = false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 加载更多数据
|
||||||
|
void _loadMoreData(int tabIndex) {
|
||||||
|
if (_isLoadingMore) return;
|
||||||
|
_loadTabData(tabIndex, isRefresh: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击关注按钮
|
||||||
|
onFocusBtnClick (user,index) async {
|
||||||
|
final vlogerId = user['id'];
|
||||||
|
final doIFollowVloger = user['doIFollowVloger'];
|
||||||
|
print('是否关注此用户------------->${doIFollowVloger}');
|
||||||
|
print('此用户UserId------------->${vlogerId}');
|
||||||
|
if (doIFollowVloger == false) {
|
||||||
|
final res = await ImService.instance.followUser(userIDList: [vlogerId]);
|
||||||
|
print('关注结果------------->${res.success}');
|
||||||
|
if (res.success) {
|
||||||
|
setState(() {
|
||||||
|
_userResults[index]['doIFollowVloger'] = !_userResults[index]['doIFollowVloger'];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
final res = await ImService.instance.followUser(userIDList: [vlogerId]);
|
||||||
|
print('取消关注结果------------->${res.success}');
|
||||||
|
if (res.success) {
|
||||||
|
setState(() {
|
||||||
|
_userResults[index]['doIFollowVloger'] = !_userResults[index]['doIFollowVloger'];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 构建加载更多组件
|
||||||
|
Widget _buildLoadMoreWidget(int tabIndex) {
|
||||||
|
if (_isLoadingMore) {
|
||||||
|
return const Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 8.0), // 减少垂直间距
|
||||||
|
child: SizedBox(
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
child: CircularProgressIndicator(strokeWidth: 2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasMore;
|
||||||
|
switch (tabIndex) {
|
||||||
|
case 0:
|
||||||
|
hasMore = _videoHasMore;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
hasMore = _productHasMore;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
hasMore = _userHasMore;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
hasMore = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasMore && _getCurrentResults(tabIndex).isNotEmpty) {
|
||||||
|
return const Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 8.0), // 减少垂直间距
|
||||||
|
child: Text(
|
||||||
|
'没有更多数据了',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12, // 减小字体
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前Tab的数据
|
||||||
|
List<dynamic> _getCurrentResults(int tabIndex) {
|
||||||
|
switch (tabIndex) {
|
||||||
|
case 0:
|
||||||
|
return _videoResults;
|
||||||
|
case 1:
|
||||||
|
return _productResults;
|
||||||
|
case 2:
|
||||||
|
return _userResults;
|
||||||
|
default:
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@ -251,16 +431,15 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
|
|||||||
padding: const EdgeInsets.only(top: 8),
|
padding: const EdgeInsets.only(top: 8),
|
||||||
child: TabBar(
|
child: TabBar(
|
||||||
controller: _tabController,
|
controller: _tabController,
|
||||||
// isScrollable: true,
|
|
||||||
indicatorColor: Colors.pink,
|
indicatorColor: Colors.pink,
|
||||||
labelColor: Colors.pink,
|
labelColor: Colors.pink,
|
||||||
unselectedLabelColor: Colors.grey,
|
unselectedLabelColor: Colors.grey,
|
||||||
labelStyle: const TextStyle(fontWeight: FontWeight.bold),
|
labelStyle: const TextStyle(fontWeight: FontWeight.bold),
|
||||||
unselectedLabelStyle: const TextStyle(fontWeight: FontWeight.normal),
|
unselectedLabelStyle: const TextStyle(fontWeight: FontWeight.normal),
|
||||||
padding: const EdgeInsets.only(left: 16), // 添加左侧内边距
|
padding: const EdgeInsets.only(left: 16),
|
||||||
indicatorPadding: EdgeInsets.zero,
|
indicatorPadding: EdgeInsets.zero,
|
||||||
labelPadding: const EdgeInsets.symmetric(horizontal: 16), // 保持标签间距
|
labelPadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
indicatorSize: TabBarIndicatorSize.label, // 改为label宽度,与文字一样宽
|
indicatorSize: TabBarIndicatorSize.label,
|
||||||
dividerColor: Colors.transparent,
|
dividerColor: Colors.transparent,
|
||||||
tabs: const [
|
tabs: const [
|
||||||
Tab(text: '视频'),
|
Tab(text: '视频'),
|
||||||
@ -292,85 +471,166 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
|
|||||||
|
|
||||||
// 视频Tab
|
// 视频Tab
|
||||||
Widget _buildVideoTab() {
|
Widget _buildVideoTab() {
|
||||||
if (_videoResults.isEmpty) {
|
if (_videoResults.isEmpty && !_isLoading) {
|
||||||
return _buildEmptyView('暂无视频结果');
|
return _buildEmptyView('暂无视频结果');
|
||||||
}
|
}
|
||||||
|
|
||||||
return ScrollConfiguration(
|
return NotificationListener<ScrollNotification>(
|
||||||
|
onNotification: (ScrollNotification scrollInfo) {
|
||||||
|
if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent &&
|
||||||
|
!_isLoadingMore &&
|
||||||
|
_videoHasMore) {
|
||||||
|
_loadMoreData(0);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: ScrollConfiguration(
|
||||||
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
||||||
child: ListView.builder(
|
child: GridView.builder(
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(8),
|
||||||
|
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: 2,
|
||||||
|
crossAxisSpacing: 8,
|
||||||
|
mainAxisSpacing: 8,
|
||||||
|
childAspectRatio: 0.65,
|
||||||
|
),
|
||||||
itemCount: _videoResults.length,
|
itemCount: _videoResults.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final video = _videoResults[index] as Map<String, dynamic>;
|
final video = _videoResults[index] as Map<String, dynamic>;
|
||||||
return _buildVideoItem(video);
|
return _buildVideoItem(video);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_buildVideoLoadMoreWidget(),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 视频项构建
|
||||||
Widget _buildVideoItem(Map<String, dynamic> video) {
|
Widget _buildVideoItem(Map<String, dynamic> video) {
|
||||||
return Container(
|
return GestureDetector(
|
||||||
margin: const EdgeInsets.only(bottom: 12),
|
onTap: () {
|
||||||
child: Row(
|
// 视频点击事件处理
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
print('点击了视频: ${video['id']}');
|
||||||
children: [
|
Get.toNamed('/videoDetail', arguments: {'videoId': video['id']});
|
||||||
// 视频封面
|
},
|
||||||
Container(
|
child: Container(
|
||||||
width: 120,
|
|
||||||
height: 80,
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(6),
|
borderRadius: BorderRadius.circular(_itemCornerRadius),
|
||||||
color: Colors.grey[200],
|
color: Colors.white,
|
||||||
image: video['cover'] != null
|
boxShadow: [
|
||||||
? DecorationImage(
|
BoxShadow(
|
||||||
image: NetworkImage(video['cover'].toString()),
|
color: Colors.grey.withOpacity(0.1),
|
||||||
fit: BoxFit.cover,
|
blurRadius: 4,
|
||||||
)
|
offset: const Offset(0, 2),
|
||||||
: null,
|
|
||||||
),
|
),
|
||||||
child: video['cover'] == null
|
],
|
||||||
? const Icon(Icons.videocam, color: Colors.grey)
|
|
||||||
: null,
|
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
|
||||||
// 视频信息
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// 视频封面 - 宽度100%,高度自适应
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(_itemCornerRadius),
|
||||||
|
topRight: Radius.circular(_itemCornerRadius),
|
||||||
|
),
|
||||||
|
child: Container(
|
||||||
|
width: double.infinity,
|
||||||
|
color: Colors.grey[200],
|
||||||
|
child: video['cover'] != null && video['cover'].toString().isNotEmpty
|
||||||
|
? Image.network(
|
||||||
|
video['cover'].toString(),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
errorBuilder: (context, error, stackTrace) {
|
||||||
|
return Container(
|
||||||
|
height: 120,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: const Icon(
|
||||||
|
Icons.videocam,
|
||||||
|
color: Colors.grey,
|
||||||
|
size: 40, // 设置一个合理的默认大小
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: Container(
|
||||||
|
height: 200,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
return Icon(
|
||||||
|
Icons.videocam,
|
||||||
|
color: Colors.grey,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// 视频信息 - 固定在容器底部
|
||||||
|
Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(6),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end, // 内容靠底部对齐
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
video['title']?.toString() ?? '无标题',
|
video['title']?.toString() ?? '无标题',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 11,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
maxLines: 2,
|
maxLines: 1, // 改为 1,限制为单行
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis, // 超出部分显示省略号
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text(
|
|
||||||
video['author']?.toString() ?? '未知作者',
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Colors.grey,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Container(
|
||||||
'#${video['tag']?.toString() ?? '无标签'}',
|
width: 18,
|
||||||
|
height: 18,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: Colors.grey[300],
|
||||||
|
image: video['avatar'] != null && video['avatar'].toString().isNotEmpty
|
||||||
|
? DecorationImage(
|
||||||
|
image: NetworkImage(video['avatar'].toString()),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
child: video['avatar'] == null || video['avatar'].toString().isEmpty
|
||||||
|
? const Icon(Icons.person, size: 10, color: Colors.grey)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
video['nickname']?.toString() ?? '未知作者',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 9,
|
||||||
color: Colors.blue,
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 6),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.favorite_border, size: 10, color: Colors.grey),
|
||||||
|
const SizedBox(width: 2),
|
||||||
Text(
|
Text(
|
||||||
video['location']?.toString() ?? '',
|
Utils().formatLikeCount(video['likeCounts'] ?? 0),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 9,
|
||||||
color: Colors.grey,
|
color: Colors.grey,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -378,38 +638,135 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 视频加载更多组件
|
||||||
|
Widget _buildVideoLoadMoreWidget() {
|
||||||
|
if (_isLoadingMore) {
|
||||||
|
return const Padding(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 8.0),
|
||||||
|
child: Center(
|
||||||
|
child: SizedBox(
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
child: CircularProgressIndicator(strokeWidth: 2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_videoHasMore && _videoResults.isNotEmpty) {
|
||||||
|
return const Padding(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 8.0),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
'没有更多数据了',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
|
||||||
// 商品Tab
|
// 商品Tab
|
||||||
Widget _buildProductTab() {
|
Widget _buildProductTab() {
|
||||||
if (_productResults.isEmpty) {
|
if (_productResults.isEmpty && !_isLoading) {
|
||||||
return _buildEmptyView('暂无商品结果');
|
return _buildEmptyView('暂无商品结果');
|
||||||
}
|
}
|
||||||
|
|
||||||
return ScrollConfiguration(
|
return NotificationListener<ScrollNotification>(
|
||||||
|
onNotification: (ScrollNotification scrollInfo) {
|
||||||
|
if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent &&
|
||||||
|
!_isLoadingMore &&
|
||||||
|
_productHasMore) {
|
||||||
|
_loadMoreData(1);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: ScrollConfiguration(
|
||||||
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
||||||
child: ListView.builder(
|
child: GridView.builder(
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(8),
|
||||||
|
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: 2,
|
||||||
|
crossAxisSpacing: 8,
|
||||||
|
mainAxisSpacing: 8,
|
||||||
|
childAspectRatio: 0.55,
|
||||||
|
),
|
||||||
itemCount: _productResults.length,
|
itemCount: _productResults.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final product = _productResults[index] as Map<String, dynamic>;
|
final product = _productResults[index] as Map<String, dynamic>;
|
||||||
return _buildProductItem(product);
|
return _buildProductItem(product);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_buildProductLoadMoreWidget(),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 商品加载更多组件
|
||||||
|
Widget _buildProductLoadMoreWidget() {
|
||||||
|
if (_isLoadingMore) {
|
||||||
|
return const Padding(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 8.0),
|
||||||
|
child: Center(
|
||||||
|
child: SizedBox(
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
child: CircularProgressIndicator(strokeWidth: 2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_productHasMore && _productResults.isNotEmpty) {
|
||||||
|
return const Padding(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 8.0),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
'没有更多数据了',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildProductItem(Map<String, dynamic> product) {
|
Widget _buildProductItem(Map<String, dynamic> product) {
|
||||||
return Container(
|
return GestureDetector(
|
||||||
margin: const EdgeInsets.only(bottom: 12),
|
onTap: () {
|
||||||
padding: const EdgeInsets.all(12),
|
// 商品点击事件处理
|
||||||
|
print('点击了商品: ${product['id']}');
|
||||||
|
Get.toNamed('/goods', arguments: {'goodsId': product['id']});
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(_itemCornerRadius),
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.grey.withOpacity(0.1),
|
color: Colors.grey.withOpacity(0.1),
|
||||||
@ -418,65 +775,64 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
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(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// 商品图片 - 宽度100%,高度自适应
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(_itemCornerRadius),
|
||||||
|
topRight: Radius.circular(_itemCornerRadius),
|
||||||
|
),
|
||||||
|
child: Container(
|
||||||
|
width: double.infinity,
|
||||||
|
color: Colors.grey[200],
|
||||||
|
child: product['pic'] != null
|
||||||
|
? Image.network(
|
||||||
|
product['pic'].toString(),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
)
|
||||||
|
: Container(
|
||||||
|
height: 110,
|
||||||
|
child: const Center(
|
||||||
|
child: Icon(Icons.shopping_bag, color: Colors.grey, size: 32),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// 商品信息 - 固定在容器底部
|
||||||
|
Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(6),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end, // 内容靠底部对齐
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
product['name']?.toString() ?? '未知商品',
|
product['name']?.toString() ?? '未知商品',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 11,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 4),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'¥${product['price']?.toString() ?? '0.00'}',
|
'¥${product['price']?.toString() ?? '0.00'}',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: Colors.pink,
|
color: Colors.pink,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 6),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Text(
|
Text(
|
||||||
'已售 ${product['sold']?.toString() ?? '0'}',
|
'已售 ${product['sales']?.toString() ?? '0'}',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 9,
|
||||||
color: Colors.grey,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Text(
|
|
||||||
'${product['comments']?.toString() ?? '0'}条评论',
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Colors.grey,
|
color: Colors.grey,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -485,41 +841,52 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// 购买按钮
|
|
||||||
IconButton(
|
|
||||||
onPressed: () {
|
|
||||||
// 跳转到商品详情
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.shopping_cart, size: 20),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用户Tab
|
// 用户Tab
|
||||||
Widget _buildUserTab() {
|
Widget _buildUserTab() {
|
||||||
if (_userResults.isEmpty) {
|
if (_userResults.isEmpty && !_isLoading) {
|
||||||
return _buildEmptyView('暂无用户结果');
|
return _buildEmptyView('暂无用户结果');
|
||||||
}
|
}
|
||||||
|
|
||||||
return ScrollConfiguration(
|
return NotificationListener<ScrollNotification>(
|
||||||
|
onNotification: (ScrollNotification scrollInfo) {
|
||||||
|
if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent &&
|
||||||
|
!_isLoadingMore &&
|
||||||
|
_userHasMore) {
|
||||||
|
_loadMoreData(2);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
child: ScrollConfiguration(
|
||||||
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(8),
|
||||||
itemCount: _userResults.length,
|
itemCount: _userResults.length + 1,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
|
if (index == _userResults.length) {
|
||||||
|
return _buildLoadMoreWidget(2);
|
||||||
|
}
|
||||||
final user = _userResults[index] as Map<String, dynamic>;
|
final user = _userResults[index] as Map<String, dynamic>;
|
||||||
return _buildUserItem(user);
|
return _buildUserItem(user,index);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildUserItem(Map<String, dynamic> user) {
|
Widget _buildUserItem(Map<String, dynamic> user, int index) {
|
||||||
|
// 判断当前用户是否已被关注
|
||||||
|
bool isFollowing = user['doIFollowVloger'] ?? false;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.only(bottom: 12),
|
margin: const EdgeInsets.only(bottom: 8),
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(10),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
@ -533,10 +900,9 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
|
|||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
// 用户头像
|
|
||||||
Container(
|
Container(
|
||||||
width: 50,
|
width: 45,
|
||||||
height: 50,
|
height: 45,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
color: Colors.grey[200],
|
color: Colors.grey[200],
|
||||||
@ -548,53 +914,55 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
|
|||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
child: user['avatar'] == null
|
child: user['avatar'] == null
|
||||||
? const Icon(Icons.person, color: Colors.grey)
|
? const Icon(Icons.person, color: Colors.grey, size: 20)
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 10),
|
||||||
// 用户信息
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
user['name']?.toString() ?? '未知用户',
|
user['nickname']?.toString() ?? '未知用户',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 13,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 2),
|
||||||
Text(
|
Text(
|
||||||
'粉丝: ${user['fans']?.toString() ?? '0'}',
|
'粉丝: ${user['fansCount']?.toString() ?? '0'}',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 11,
|
||||||
color: Colors.grey,
|
color: Colors.grey,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// 关注按钮
|
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
// 关注用户
|
await onFocusBtnClick(user, index);
|
||||||
},
|
},
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: Colors.pink,
|
backgroundColor: isFollowing ? Colors.grey[300] : Colors.pink,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: isFollowing ? Colors.grey[600] : Colors.white,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(16),
|
||||||
),
|
),
|
||||||
|
// 添加边框样式
|
||||||
|
side: isFollowing
|
||||||
|
? BorderSide(color: Colors.grey[400]!, width: 0.5)
|
||||||
|
: BorderSide.none,
|
||||||
),
|
),
|
||||||
child: const Text(
|
child: Text(
|
||||||
'关注',
|
isFollowing ? '已关注' : '关注',
|
||||||
style: TextStyle(fontSize: 12),
|
style: const TextStyle(fontSize: 11),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -604,12 +972,12 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const Icon(Icons.search_off, size: 60, color: Colors.grey),
|
const Icon(Icons.search_off, size: 50, color: Colors.grey),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 12),
|
||||||
Text(
|
Text(
|
||||||
message,
|
message,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 14,
|
||||||
color: Colors.grey,
|
color: Colors.grey,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
1404
lib/pages/video/commonVideo.dart
Normal file
1404
lib/pages/video/commonVideo.dart
Normal file
File diff suppressed because it is too large
Load Diff
@ -18,6 +18,7 @@ import 'package:loopin/pages/my/vloger.dart';
|
|||||||
import 'package:loopin/pages/video/report.dart';
|
import 'package:loopin/pages/video/report.dart';
|
||||||
import 'package:loopin/pages/search/index.dart';
|
import 'package:loopin/pages/search/index.dart';
|
||||||
import 'package:loopin/pages/search/search-result.dart';
|
import 'package:loopin/pages/search/search-result.dart';
|
||||||
|
import 'package:loopin/pages/video/commonVideo.dart';
|
||||||
|
|
||||||
import '../layouts/index.dart';
|
import '../layouts/index.dart';
|
||||||
/* 引入路由页面 */
|
/* 引入路由页面 */
|
||||||
@ -40,6 +41,7 @@ final Map<String, Widget> routes = {
|
|||||||
'/order/detail': const OrderDetail(),
|
'/order/detail': const OrderDetail(),
|
||||||
'/vloger': const Vloger(),
|
'/vloger': const Vloger(),
|
||||||
'/report': const ReportPage(),
|
'/report': const ReportPage(),
|
||||||
|
'/videoDetail': const VideoDetailPage(),
|
||||||
'/search': const SearchPage(),
|
'/search': const SearchPage(),
|
||||||
'/search-result': const SearchResultPage(),
|
'/search-result': const SearchResultPage(),
|
||||||
//settins
|
//settins
|
||||||
|
@ -199,4 +199,13 @@ class Utils {
|
|||||||
const weekdays = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"];
|
const weekdays = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"];
|
||||||
return weekdays[(weekday - 1) % 7];
|
return weekdays[(weekday - 1) % 7];
|
||||||
}
|
}
|
||||||
|
// 格式化点赞数量
|
||||||
|
String formatLikeCount(int count) {
|
||||||
|
if (count >= 10000) {
|
||||||
|
return '${(count / 10000).toStringAsFixed(1)}w';
|
||||||
|
} else if (count >= 1000) {
|
||||||
|
return '${(count / 1000).toStringAsFixed(1)}k';
|
||||||
|
}
|
||||||
|
return count.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user