Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
e90837a9b7
@ -1,6 +1,8 @@
|
|||||||
/// 单图/多图预览查看器
|
/// 单图/多图预览查看器
|
||||||
library;
|
library;
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:photo_view/photo_view.dart';
|
import 'package:photo_view/photo_view.dart';
|
||||||
@ -31,6 +33,19 @@ class _ImageViewerState extends State<ImageViewer> {
|
|||||||
currentIndex = widget.index;
|
currentIndex = widget.index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImageProvider getImageProvider(String path) {
|
||||||
|
if (Utils.isUrl(path)) {
|
||||||
|
// 网络图片
|
||||||
|
return NetworkImage(path);
|
||||||
|
} else if (File(path).existsSync()) {
|
||||||
|
// 本地文件路径
|
||||||
|
return FileImage(File(path));
|
||||||
|
} else {
|
||||||
|
// assets 资源
|
||||||
|
return AssetImage(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var imgCount = widget.images?.length;
|
var imgCount = widget.images?.length;
|
||||||
@ -44,39 +59,40 @@ class _ImageViewerState extends State<ImageViewer> {
|
|||||||
bottom: 0,
|
bottom: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
child: imgCount == 1 ? PhotoView(
|
child: imgCount == 1
|
||||||
imageProvider: Utils.isUrl(widget.images![0]) ? NetworkImage(widget.images![0]) : AssetImage(widget.images![0]),
|
? PhotoView(
|
||||||
backgroundDecoration: const BoxDecoration(
|
// imageProvider: Utils.isUrl(widget.images![0]) ? NetworkImage(widget.images![0]) : AssetImage(widget.images![0]),
|
||||||
color: Colors.black,
|
imageProvider: getImageProvider(widget.images![0]),
|
||||||
),
|
backgroundDecoration: const BoxDecoration(
|
||||||
minScale: PhotoViewComputedScale.contained,
|
color: Colors.black,
|
||||||
maxScale: PhotoViewComputedScale.covered * 2,
|
),
|
||||||
heroAttributes: PhotoViewHeroAttributes(tag: widget.images![0]),
|
minScale: PhotoViewComputedScale.contained,
|
||||||
enableRotation: true,
|
maxScale: PhotoViewComputedScale.covered * 2,
|
||||||
)
|
heroAttributes: PhotoViewHeroAttributes(tag: widget.images![0]),
|
||||||
:
|
enableRotation: true,
|
||||||
PhotoViewGallery.builder(
|
)
|
||||||
itemCount: widget.images?.length,
|
: PhotoViewGallery.builder(
|
||||||
builder: (context, index) {
|
itemCount: widget.images?.length,
|
||||||
return PhotoViewGalleryPageOptions(
|
builder: (context, index) {
|
||||||
imageProvider: Utils.isUrl(widget.images![index]) ? NetworkImage(widget.images![index]) : AssetImage(widget.images![index]),
|
return PhotoViewGalleryPageOptions(
|
||||||
minScale: PhotoViewComputedScale.contained,
|
imageProvider: Utils.isUrl(widget.images![index]) ? NetworkImage(widget.images![index]) : AssetImage(widget.images![index]),
|
||||||
maxScale: PhotoViewComputedScale.covered * 2,
|
minScale: PhotoViewComputedScale.contained,
|
||||||
heroAttributes: PhotoViewHeroAttributes(tag: widget.images![index]),
|
maxScale: PhotoViewComputedScale.covered * 2,
|
||||||
);
|
heroAttributes: PhotoViewHeroAttributes(tag: widget.images![index]),
|
||||||
},
|
);
|
||||||
scrollPhysics: const BouncingScrollPhysics(),
|
},
|
||||||
backgroundDecoration: const BoxDecoration(
|
scrollPhysics: const BouncingScrollPhysics(),
|
||||||
color: Colors.black,
|
backgroundDecoration: const BoxDecoration(
|
||||||
),
|
color: Colors.black,
|
||||||
pageController: PageController(initialPage: widget.index),
|
),
|
||||||
enableRotation: true,
|
pageController: PageController(initialPage: widget.index),
|
||||||
onPageChanged: (index) {
|
enableRotation: true,
|
||||||
setState(() {
|
onPageChanged: (index) {
|
||||||
currentIndex = index;
|
setState(() {
|
||||||
});
|
currentIndex = index;
|
||||||
},
|
});
|
||||||
),
|
},
|
||||||
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Get.back();
|
Get.back();
|
||||||
},
|
},
|
||||||
@ -87,11 +103,13 @@ class _ImageViewerState extends State<ImageViewer> {
|
|||||||
top: MediaQuery.of(context).padding.top + 15,
|
top: MediaQuery.of(context).padding.top + 15,
|
||||||
width: MediaQuery.of(context).size.width,
|
width: MediaQuery.of(context).size.width,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Visibility(
|
child: Visibility(
|
||||||
visible: imgCount! > 1 ? true : false,
|
visible: imgCount! > 1 ? true : false,
|
||||||
child: Text('${currentIndex+1} / ${widget.images?.length}', style: const TextStyle(color: Colors.white, fontSize: 16, fontFamily: 'arial'),),
|
child: Text(
|
||||||
)
|
'${currentIndex + 1} / ${widget.images?.length}',
|
||||||
),
|
style: const TextStyle(color: Colors.white, fontSize: 16, fontFamily: 'arial'),
|
||||||
|
),
|
||||||
|
)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -52,7 +52,7 @@ conversationTypeFromString(String? type) {
|
|||||||
return ConversationType.order.name;
|
return ConversationType.order.name;
|
||||||
} else if (type.contains('groupNotify')) {
|
} else if (type.contains('groupNotify')) {
|
||||||
return ConversationType.groupNotify.name;
|
return ConversationType.groupNotify.name;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
@ -527,6 +527,7 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
|
|||||||
else if (item.elemType == 2 && item.cloudCustomData == SummaryType.shareTuangou) {
|
else if (item.elemType == 2 && item.cloudCustomData == SummaryType.shareTuangou) {
|
||||||
//price,title,url,sell
|
//price,title,url,sell
|
||||||
final obj = jsonDecode(item.customElem!.data!);
|
final obj = jsonDecode(item.customElem!.data!);
|
||||||
|
logger.e(obj);
|
||||||
final url = obj['url'];
|
final url = obj['url'];
|
||||||
final title = obj['title'];
|
final title = obj['title'];
|
||||||
final price = obj['price'];
|
final price = obj['price'];
|
||||||
@ -607,6 +608,7 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
|
|||||||
else if (item.elemType == 2 && item.cloudCustomData == SummaryType.shareVideo) {
|
else if (item.elemType == 2 && item.cloudCustomData == SummaryType.shareVideo) {
|
||||||
/// {imgUrl,videoUrl,width,height}
|
/// {imgUrl,videoUrl,width,height}
|
||||||
final obj = jsonDecode(item.customElem!.data!);
|
final obj = jsonDecode(item.customElem!.data!);
|
||||||
|
logger.e(obj);
|
||||||
final videoUrl = obj['videoUrl'];
|
final videoUrl = obj['videoUrl'];
|
||||||
final imgUrl = obj['imgUrl'];
|
final imgUrl = obj['imgUrl'];
|
||||||
final width = obj['width'] as num;
|
final width = obj['width'] as num;
|
||||||
|
@ -13,6 +13,7 @@ import 'package:loopin/IM/im_service.dart';
|
|||||||
import 'package:loopin/api/shop_api.dart';
|
import 'package:loopin/api/shop_api.dart';
|
||||||
import 'package:loopin/components/my_toast.dart';
|
import 'package:loopin/components/my_toast.dart';
|
||||||
import 'package:loopin/components/network_or_asset_image.dart';
|
import 'package:loopin/components/network_or_asset_image.dart';
|
||||||
|
import 'package:loopin/models/conversation_type.dart';
|
||||||
import 'package:loopin/models/share_type.dart';
|
import 'package:loopin/models/share_type.dart';
|
||||||
import 'package:loopin/models/summary_type.dart';
|
import 'package:loopin/models/summary_type.dart';
|
||||||
import 'package:loopin/service/http.dart';
|
import 'package:loopin/service/http.dart';
|
||||||
@ -48,7 +49,7 @@ class _GoodsState extends State<Goods> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
final goodsId = Get.arguments['goodsId'];
|
final goodsId = Get.arguments['goodsId'] ?? '';
|
||||||
scrollController.addListener(() {
|
scrollController.addListener(() {
|
||||||
setState(() {
|
setState(() {
|
||||||
scrollOffset = scrollController.offset;
|
scrollOffset = scrollController.offset;
|
||||||
@ -78,25 +79,22 @@ class _GoodsState extends State<Goods> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
///创建定点杆
|
///创建定点杆
|
||||||
createOrder(String goodsId) async {
|
createOrder(String goodsId) async {
|
||||||
var params ={
|
var params = {
|
||||||
"type": 1, // 订单类型:1->团购;2->拼团;3->秒杀;
|
"type": 1, // 订单类型:1->团购;2->拼团;3->秒杀;
|
||||||
"distribution": 1, // 配送方式 1->到店核销;2->自提;3->配送;
|
"distribution": 1, // 配送方式 1->到店核销;2->自提;3->配送;
|
||||||
"skuItemBOList": [
|
"skuItemBOList": [
|
||||||
{
|
{"skuId": goodsId, "quantity": 1}
|
||||||
"skuId": goodsId,
|
]
|
||||||
"quantity": 1
|
};
|
||||||
}
|
print('下单请求参数---->$params');
|
||||||
]
|
|
||||||
};
|
|
||||||
print('下单请求参数---->${params}');
|
|
||||||
try {
|
try {
|
||||||
final res = await Http.post('${ShopApi.createGoodsOrder}', data: params);
|
final res = await Http.post(ShopApi.createGoodsOrder, data: params);
|
||||||
var resData = res['data'];
|
var resData = res['data'];
|
||||||
print('1111111111111111111111111---->${res}');
|
print('1111111111111111111111111---->$res');
|
||||||
if(resData['id'].isNotEmpty){
|
if (resData['id'].isNotEmpty) {
|
||||||
return resData['id'];
|
return resData['id'];
|
||||||
}else{
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -104,6 +102,7 @@ class _GoodsState extends State<Goods> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleShareClick(int index) {
|
void handleShareClick(int index) {
|
||||||
final description = shopObj['describe']; // 商品描述
|
final description = shopObj['describe']; // 商品描述
|
||||||
if (index == 1) {
|
if (index == 1) {
|
||||||
@ -116,12 +115,13 @@ class _GoodsState extends State<Goods> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void handlCoverClick(V2TimConversation conv) async {
|
void handlCoverClick(V2TimConversation conv) async {
|
||||||
// 发送VideoMsg,获取当前视频信息
|
// 发送自定义消息 商品信息
|
||||||
final userId = conv.userID;
|
final userId = conv.userID;
|
||||||
//price,title,url,sell
|
//price,title,url,sell
|
||||||
|
logger.w(shopObj['name']);
|
||||||
final makeJson = jsonEncode({
|
final makeJson = jsonEncode({
|
||||||
"price": shopObj['price'],
|
"price": shopObj['price'],
|
||||||
"title": shopObj['describe'],
|
"title": shopObj['name'],
|
||||||
"url": shopObj['pic'],
|
"url": shopObj['pic'],
|
||||||
"sell": Utils.graceNumber(int.parse(shopObj['sales'] ?? '0')),
|
"sell": Utils.graceNumber(int.parse(shopObj['sales'] ?? '0')),
|
||||||
});
|
});
|
||||||
@ -200,7 +200,8 @@ class _GoodsState extends State<Goods> {
|
|||||||
// 会话列表
|
// 会话列表
|
||||||
Obx(() {
|
Obx(() {
|
||||||
// 这里过滤掉有分组的会话
|
// 这里过滤掉有分组的会话
|
||||||
final filteredList = chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).toList();
|
final filteredList = chatController.chatList.where((item) => conversationTypeFromString(item.isCustomAdmin) == null).toList();
|
||||||
|
|
||||||
if (filteredList.isEmpty) return SizedBox.shrink();
|
if (filteredList.isEmpty) return SizedBox.shrink();
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: 110,
|
height: 110,
|
||||||
@ -219,10 +220,12 @@ class _GoodsState extends State<Goods> {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
// Image.asset('${chatController.chatList[index].faceUrl}', width: 48.0),
|
// Image.asset('${chatController.chatList[index].faceUrl}', width: 48.0),
|
||||||
NetworkOrAssetImage(
|
ClipOval(
|
||||||
imageUrl: filteredList[index].faceUrl,
|
child: NetworkOrAssetImage(
|
||||||
width: 48.0,
|
imageUrl: filteredList[index].faceUrl,
|
||||||
height: 48.0,
|
width: 48.0,
|
||||||
|
height: 48.0,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 5),
|
SizedBox(height: 5),
|
||||||
Text(
|
Text(
|
||||||
@ -338,7 +341,12 @@ class _GoodsState extends State<Goods> {
|
|||||||
activeColor: Colors.white,
|
activeColor: Colors.white,
|
||||||
)),
|
)),
|
||||||
indicatorLayout: PageIndicatorLayout.SCALE,
|
indicatorLayout: PageIndicatorLayout.SCALE,
|
||||||
children: swiperList.map((sw) => NetworkOrAssetImage(imageUrl: sw)).toList(),
|
children: swiperList
|
||||||
|
.map((sw) => NetworkOrAssetImage(
|
||||||
|
imageUrl: sw,
|
||||||
|
placeholderAsset: 'assets/images/bk.jpg',
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -577,35 +585,46 @@ class _GoodsState extends State<Goods> {
|
|||||||
// )
|
// )
|
||||||
// ],
|
// ],
|
||||||
// ),
|
// ),
|
||||||
GestureDetector(
|
|
||||||
onTap: () async {
|
// 联系商家
|
||||||
// 可以在这里打开聊天
|
// GestureDetector(
|
||||||
logger.i('联系客服');
|
// onTap: () async {
|
||||||
final res = await ImService.instance.getConversation(conversationID: 'c2c_${shopObj['tenantId']}');
|
// // 可以在这里打开聊天
|
||||||
V2TimConversation conversation = res.data;
|
// logger.i('联系客服:$shopObj');
|
||||||
logger.i(conversation.toLogString());
|
// final res = await ImService.instance.getConversation(conversationID: 'c2c_${shopObj['tenantId']}');
|
||||||
if (res.success) {
|
// V2TimConversation conversation = res.data;
|
||||||
// 客服聊天不用检测关注关系
|
// logger.i(conversation.toLogString());
|
||||||
conversation.showName = conversation.showName ?? shopObj['storeName'];
|
// if (res.success) {
|
||||||
Get.toNamed('/chat', arguments: conversation);
|
// // 客服聊天不用检测关注关系
|
||||||
} else {
|
// V2TimUserFullInfo? sellerInfo;
|
||||||
MyDialog.toast(res.desc, icon: const Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
// final resIm = await ImService.instance.otherInfo(shopObj['tenantId']);
|
||||||
}
|
// if (resIm.success && resIm.data != null) {
|
||||||
},
|
// sellerInfo = resIm.data!;
|
||||||
child: Column(
|
// logger.i(sellerInfo!.toLogString());
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
// } else {
|
||||||
children: [
|
// logger.e(resIm.desc);
|
||||||
Icon(
|
// }
|
||||||
Icons.child_care_outlined,
|
// conversation.showName = conversation.showName ?? sellerInfo!.nickName;
|
||||||
size: 18.0,
|
// Get.toNamed('/chat', arguments: conversation);
|
||||||
),
|
// } else {
|
||||||
Text(
|
// MyDialog.toast(res.desc, icon: const Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
||||||
'联系商家',
|
// }
|
||||||
style: TextStyle(fontSize: 12.0),
|
// },
|
||||||
)
|
// child: Column(
|
||||||
],
|
// mainAxisAlignment: MainAxisAlignment.center,
|
||||||
),
|
// children: [
|
||||||
)
|
// Icon(
|
||||||
|
// Icons.child_care_outlined,
|
||||||
|
// size: 18.0,
|
||||||
|
// ),
|
||||||
|
// Text(
|
||||||
|
// '联系商家',
|
||||||
|
// style: TextStyle(fontSize: 12.0),
|
||||||
|
// )
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// )
|
||||||
|
|
||||||
// Column(
|
// Column(
|
||||||
// mainAxisAlignment: MainAxisAlignment.center,
|
// mainAxisAlignment: MainAxisAlignment.center,
|
||||||
// children: [
|
// children: [
|
||||||
@ -650,11 +669,11 @@ class _GoodsState extends State<Goods> {
|
|||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
// 这里走生成预支付订单,拿到orderId
|
// 这里走生成预支付订单,拿到orderId
|
||||||
// String orderId = '1958380183857659904'; //测试数据
|
// String orderId = '1958380183857659904'; //测试数据
|
||||||
String orderId = await createOrder(shopObj['skuList'][0]['id']);
|
String orderId = await createOrder(shopObj['skuList'][0]['id']);
|
||||||
if(orderId.isNotEmpty){
|
if (orderId.isNotEmpty) {
|
||||||
Get.toNamed('/order/detail', arguments: orderId);
|
Get.toNamed('/order/detail', arguments: orderId);
|
||||||
}else{
|
} else {
|
||||||
MyDialog.toast('生成订单失败', icon: const Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
MyDialog.toast('生成订单失败', icon: const Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -182,7 +182,7 @@ class FansState extends State<Fans> with SingleTickerProviderStateMixin {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
Get.toNamed(
|
Get.toNamed(
|
||||||
'/vloger',
|
'/vloger',
|
||||||
arguments: item.userInfo.userID,
|
arguments: {'memberId': item.userInfo.userID},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: Row(
|
child: Row(
|
||||||
|
@ -182,7 +182,7 @@ class FlowingState extends State<Flowing> with SingleTickerProviderStateMixin {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
Get.toNamed(
|
Get.toNamed(
|
||||||
'/vloger',
|
'/vloger',
|
||||||
arguments: item.userInfo.userID,
|
arguments: {'memberId': item.userInfo.userID},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: Row(
|
child: Row(
|
||||||
|
@ -182,7 +182,7 @@ class MutualFollowersState extends State<MutualFollowers> with SingleTickerProvi
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
Get.toNamed(
|
Get.toNamed(
|
||||||
'/vloger',
|
'/vloger',
|
||||||
arguments: item.userInfo.userID,
|
arguments: {'memberId': item.userInfo.userID},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: Row(
|
child: Row(
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:loopin/IM/im_service.dart';
|
||||||
import 'package:loopin/api/common_api.dart';
|
import 'package:loopin/api/common_api.dart';
|
||||||
import 'package:loopin/api/video_api.dart';
|
import 'package:loopin/api/video_api.dart';
|
||||||
|
import 'package:loopin/components/image_viewer.dart';
|
||||||
|
import 'package:loopin/components/preview_video.dart';
|
||||||
import 'package:loopin/service/http.dart';
|
import 'package:loopin/service/http.dart';
|
||||||
import 'package:loopin/utils/index.dart';
|
import 'package:loopin/utils/index.dart';
|
||||||
|
import 'package:loopin/utils/snapshot.dart';
|
||||||
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
|
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
|
||||||
|
|
||||||
class UploadVideoPage extends StatefulWidget {
|
class UploadVideoPage extends StatefulWidget {
|
||||||
@ -19,15 +22,28 @@ class UploadVideoPage extends StatefulWidget {
|
|||||||
class _UploadVideoPageState extends State<UploadVideoPage> {
|
class _UploadVideoPageState extends State<UploadVideoPage> {
|
||||||
final selectedVideo = Rxn<AssetEntity>();
|
final selectedVideo = Rxn<AssetEntity>();
|
||||||
final selectedCover = Rxn<AssetEntity>();
|
final selectedCover = Rxn<AssetEntity>();
|
||||||
|
//视频
|
||||||
final uploading = false.obs;
|
final uploading = false.obs;
|
||||||
final uploadProgress = 0.0.obs;
|
final uploadProgress = 0.0.obs;
|
||||||
final status = ''.obs;
|
final status = ''.obs;
|
||||||
final descController = TextEditingController();
|
//图片
|
||||||
|
final uploading2 = false.obs;
|
||||||
|
final uploadProgress2 = 0.0.obs;
|
||||||
|
final status2 = ''.obs;
|
||||||
|
//地址
|
||||||
|
final uploadedVideoUrl = ''.obs; // 网络视频地址
|
||||||
|
final uploadedImgUrl = ''.obs; // 网络图片地址
|
||||||
|
final videoPath = ''.obs; // 本地视频地址
|
||||||
|
final imgPath = ''.obs; //本地图片地址
|
||||||
|
final snapshot = ''.obs;
|
||||||
|
|
||||||
final FocusNode descFocusNode = FocusNode();
|
final FocusNode descFocusNode = FocusNode();
|
||||||
final uploadedVideoUrl = ''.obs;
|
|
||||||
final TextEditingController descriptionController = TextEditingController();
|
final TextEditingController descriptionController = TextEditingController();
|
||||||
|
|
||||||
Future<void> pickVideo() async {
|
Future<void> pickVideo() async {
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
|
||||||
final result = await PhotoManager.requestPermissionExtend();
|
final result = await PhotoManager.requestPermissionExtend();
|
||||||
|
|
||||||
if (!result.isAuth) {
|
if (!result.isAuth) {
|
||||||
@ -60,10 +76,14 @@ class _UploadVideoPageState extends State<UploadVideoPage> {
|
|||||||
if (pickedAssets != null && pickedAssets.isNotEmpty) {
|
if (pickedAssets != null && pickedAssets.isNotEmpty) {
|
||||||
selectedVideo.value = pickedAssets.first;
|
selectedVideo.value = pickedAssets.first;
|
||||||
status.value = '已选择视频';
|
status.value = '已选择视频';
|
||||||
|
uploadVideo();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 选择封面图
|
||||||
Future<void> pickCoverImage() async {
|
Future<void> pickCoverImage() async {
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
|
||||||
final result = await PhotoManager.requestPermissionExtend();
|
final result = await PhotoManager.requestPermissionExtend();
|
||||||
|
|
||||||
if (!result.isAuth) {
|
if (!result.isAuth) {
|
||||||
@ -88,30 +108,73 @@ class _UploadVideoPageState extends State<UploadVideoPage> {
|
|||||||
|
|
||||||
if (pickedAssets != null && pickedAssets.isNotEmpty) {
|
if (pickedAssets != null && pickedAssets.isNotEmpty) {
|
||||||
selectedCover.value = pickedAssets.first;
|
selectedCover.value = pickedAssets.first;
|
||||||
|
// 执行上传图片逻辑
|
||||||
|
uploadImg();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 上传图片
|
||||||
|
Future<void> uploadImg() async {
|
||||||
|
final coverFile = await selectedCover.value?.file;
|
||||||
|
|
||||||
|
if (coverFile == null) {
|
||||||
|
status.value = '未选择封面图';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
imgPath.value = coverFile.path;
|
||||||
|
uploading2.value = true;
|
||||||
|
uploadProgress2.value = 0;
|
||||||
|
status2.value = '上传中...';
|
||||||
|
|
||||||
|
try {
|
||||||
|
final res = await Http.upload(
|
||||||
|
CommonApi.uploadFile,
|
||||||
|
filePath: coverFile.path,
|
||||||
|
fileKey: 'file',
|
||||||
|
onSendProgress: (sent, total) {
|
||||||
|
if (total > 0) {
|
||||||
|
uploadProgress2.value = sent / total;
|
||||||
|
if (sent == total) uploading2.value = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
uploadedImgUrl.value = res['data']['url'];
|
||||||
|
status2.value = '上传成功';
|
||||||
|
} catch (e) {
|
||||||
|
if (e is SocketException) {
|
||||||
|
status2.value = '网络错误,请检查连接';
|
||||||
|
} else {
|
||||||
|
status2.value = '上传失败: ${e.toString()}';
|
||||||
|
}
|
||||||
|
descFocusNode.unfocus();
|
||||||
|
uploading2.value = false;
|
||||||
|
} finally {
|
||||||
|
descFocusNode.unfocus();
|
||||||
|
uploading2.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传视频
|
||||||
Future<void> uploadVideo() async {
|
Future<void> uploadVideo() async {
|
||||||
final file = await selectedVideo.value?.file;
|
final file = await selectedVideo.value?.file;
|
||||||
final coverFile = await selectedCover.value?.file;
|
|
||||||
final desc = descController.text.trim();
|
|
||||||
|
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
status.value = '未选择视频';
|
status.value = '未选择视频';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (desc.isEmpty) {
|
|
||||||
Get.snackbar('请填写描述', '视频描述不能为空');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uploading.value = true;
|
uploading.value = true;
|
||||||
uploadProgress.value = 0;
|
uploadProgress.value = 0;
|
||||||
status.value = '上传中...';
|
status.value = '上传中...';
|
||||||
|
videoPath.value = file.path;
|
||||||
|
logger.w(videoPath.value);
|
||||||
|
|
||||||
|
snapshot.value = (await generateVideoThumbnail(file.path))!;
|
||||||
|
logger.w(snapshot.value);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final data = await Http.upload(
|
final res = await Http.upload(
|
||||||
CommonApi.uploadFile,
|
CommonApi.uploadFile,
|
||||||
filePath: file.path,
|
filePath: file.path,
|
||||||
fileKey: 'file',
|
fileKey: 'file',
|
||||||
@ -121,60 +184,98 @@ class _UploadVideoPageState extends State<UploadVideoPage> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
uploadedVideoUrl.value = data;
|
uploadedVideoUrl.value = res['data']['url'];
|
||||||
status.value = '上传成功';
|
status.value = '上传成功';
|
||||||
// 清空表单
|
|
||||||
descController.clear();
|
|
||||||
selectedVideo.value = null;
|
|
||||||
selectedCover.value = null;
|
|
||||||
uploadedVideoUrl.value = '';
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
descFocusNode.unfocus();
|
||||||
if (e is SocketException) {
|
if (e is SocketException) {
|
||||||
status.value = '网络错误,请检查连接';
|
status.value = '网络错误,请检查连接';
|
||||||
} else {
|
} else {
|
||||||
status.value = '上传失败: ${e.toString()}';
|
status.value = '上传失败: ${e.toString()}';
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
descFocusNode.unfocus();
|
||||||
uploading.value = false;
|
uploading.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 发布
|
||||||
Future<void> submitForm() async {
|
Future<void> submitForm() async {
|
||||||
|
logger.w(descriptionController.text);
|
||||||
|
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
if (uploadedVideoUrl.value.isEmpty) {
|
if (uploadedVideoUrl.value.isEmpty) {
|
||||||
Get.snackbar('请先上传视频', '未检测到上传的视频');
|
Get.snackbar('请先上传视频', '未检测到上传的视频');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (descriptionController.text.trim().isEmpty) {
|
if (descriptionController.text.trim().isEmpty) {
|
||||||
Get.snackbar('请填写视频描述', '描述是必填项');
|
Get.snackbar('请填写视频描述', '描述是必填项');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Http.post(VideoApi.publish, data: {
|
final data = {
|
||||||
'video_url': uploadedVideoUrl.value,
|
'url': uploadedVideoUrl.value,
|
||||||
'desc': descriptionController.text.trim(),
|
'title': descriptionController.text.trim(),
|
||||||
'cover': selectedCover.value,
|
};
|
||||||
});
|
if (uploadedImgUrl.value.isNotEmpty) {
|
||||||
Get.snackbar('发布成功', '视频已成功发布');
|
data['cover'] = uploadedImgUrl.value;
|
||||||
|
}
|
||||||
|
logger.w('发布内容:$data');
|
||||||
|
await Http.post(VideoApi.publish, data: data);
|
||||||
|
Get.snackbar('发布成功', '视频正在审核中');
|
||||||
clearForm();
|
clearForm();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Get.snackbar('发布失败', '$e');
|
Get.snackbar('发布失败', '$e');
|
||||||
}
|
} finally {}
|
||||||
}
|
}
|
||||||
|
|
||||||
void clearForm() {
|
void clearForm() {
|
||||||
selectedVideo.value = null;
|
selectedVideo.value = null;
|
||||||
selectedCover.value = null;
|
selectedCover.value = null;
|
||||||
descriptionController.clear();
|
uploadedImgUrl.value = '';
|
||||||
status.value = '';
|
|
||||||
uploadProgress.value = 0.0;
|
|
||||||
uploadedVideoUrl.value = '';
|
uploadedVideoUrl.value = '';
|
||||||
|
descriptionController.clear();
|
||||||
|
descFocusNode.unfocus();
|
||||||
|
status.value = '';
|
||||||
|
status2.value = '';
|
||||||
|
uploadProgress.value = 0.0;
|
||||||
|
uploadProgress2.value = 0.0;
|
||||||
|
videoPath.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预览视频
|
||||||
|
void openVd() {
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
showGeneralDialog(
|
||||||
|
context: context,
|
||||||
|
// barrierDismissible: true,
|
||||||
|
barrierColor: Colors.black.withAlpha((1.0 * 255).round()),
|
||||||
|
pageBuilder: (_, __, ___) {
|
||||||
|
return SafeArea(
|
||||||
|
child: PreviewVideo(
|
||||||
|
videoUrl: videoPath.value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
transitionBuilder: (_, anim, __, child) {
|
||||||
|
return FadeTransition(opacity: anim, child: child);
|
||||||
|
},
|
||||||
|
transitionDuration: const Duration(milliseconds: 200),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void openImg() {
|
||||||
|
// 图片预览
|
||||||
|
Get.to(() => ImageViewer(
|
||||||
|
images: [imgPath.value],
|
||||||
|
index: 0,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
descController.dispose();
|
descriptionController.dispose();
|
||||||
descFocusNode.dispose();
|
descFocusNode.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
@ -193,7 +294,8 @@ class _UploadVideoPageState extends State<UploadVideoPage> {
|
|||||||
children: [
|
children: [
|
||||||
TextField(
|
TextField(
|
||||||
focusNode: descFocusNode,
|
focusNode: descFocusNode,
|
||||||
controller: descController,
|
autofocus: false,
|
||||||
|
controller: descriptionController,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: '视频描述 *',
|
labelText: '视频描述 *',
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
@ -201,72 +303,125 @@ class _UploadVideoPageState extends State<UploadVideoPage> {
|
|||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
|
// 视频
|
||||||
Obx(() {
|
Obx(() {
|
||||||
final video = selectedVideo.value;
|
|
||||||
return Row(
|
return Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
|
// 左侧封面预览
|
||||||
|
snapshot.value.isNotEmpty
|
||||||
|
? GestureDetector(
|
||||||
|
onTap: () => openVd(),
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
child: Image.file(
|
||||||
|
File(snapshot.value),
|
||||||
|
width: 80,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: const SizedBox(
|
||||||
|
height: 80,
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
'未选择视频',
|
||||||
|
style: TextStyle(fontSize: 16),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(width: 24),
|
||||||
|
|
||||||
|
// 右侧按钮/进度条
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: uploading.value
|
||||||
video != null ? '已选择视频:${video.title}' : '未选择视频',
|
? Column(
|
||||||
style: const TextStyle(fontSize: 16),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
),
|
children: [
|
||||||
),
|
const CircularProgressIndicator(),
|
||||||
ElevatedButton(
|
const SizedBox(height: 10),
|
||||||
onPressed: pickVideo,
|
LinearProgressIndicator(value: uploadProgress.value),
|
||||||
child: const Text('选择视频'),
|
const SizedBox(height: 5),
|
||||||
|
Text('${(uploadProgress.value * 100).toStringAsFixed(1)}%'),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: ElevatedButton.icon(
|
||||||
|
onPressed: pickVideo,
|
||||||
|
icon: const Icon(Icons.upload),
|
||||||
|
label: Text(uploadedVideoUrl.value.isNotEmpty ? '重新选择' : '选择视频'),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
minimumSize: const Size.fromHeight(50),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
|
// 图片上传
|
||||||
Obx(() {
|
Obx(() {
|
||||||
final cover = selectedCover.value;
|
|
||||||
return Row(
|
return Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
cover != null
|
// 左侧图片预览
|
||||||
? FutureBuilder<Uint8List?>(
|
imgPath.value.isNotEmpty
|
||||||
future: cover.thumbnailDataWithSize(const ThumbnailSize(80, 80)),
|
? GestureDetector(
|
||||||
builder: (_, snapshot) {
|
onTap: () => openImg(),
|
||||||
if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
|
child: ClipRRect(
|
||||||
return ClipRRect(
|
borderRadius: BorderRadius.circular(8),
|
||||||
borderRadius: BorderRadius.circular(8),
|
child: Image.file(
|
||||||
child: Image.memory(snapshot.data!, width: 80, height: 80),
|
File(imgPath.value),
|
||||||
);
|
width: 80,
|
||||||
} else {
|
fit: BoxFit.cover,
|
||||||
return const SizedBox(width: 80, height: 80);
|
),
|
||||||
}
|
),
|
||||||
},
|
|
||||||
)
|
)
|
||||||
: const Text('未选择封面图'),
|
: const SizedBox(
|
||||||
const SizedBox(width: 12),
|
height: 80,
|
||||||
ElevatedButton(
|
child: Center(
|
||||||
onPressed: pickCoverImage,
|
child: Text(
|
||||||
child: const Text('选择封面(可选)'),
|
'未选择封面',
|
||||||
|
style: TextStyle(fontSize: 16),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(width: 24),
|
||||||
|
|
||||||
|
// 右侧按钮/进度条
|
||||||
|
Expanded(
|
||||||
|
child: uploading2.value
|
||||||
|
? Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const CircularProgressIndicator(),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
LinearProgressIndicator(value: uploadProgress2.value),
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
Text('${(uploadProgress2.value * 100).toStringAsFixed(1)}%'),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: ElevatedButton.icon(
|
||||||
|
onPressed: pickCoverImage,
|
||||||
|
icon: const Icon(Icons.upload),
|
||||||
|
label: Text(uploadedImgUrl.value.isNotEmpty ? '重新选择' : '选择封面(可选)'),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
minimumSize: const Size.fromHeight(50),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
const SizedBox(height: 30),
|
const SizedBox(height: 30),
|
||||||
Obx(() => uploading.value
|
// 发布按钮
|
||||||
? Column(
|
SizedBox(
|
||||||
children: [
|
width: double.infinity,
|
||||||
const CircularProgressIndicator(),
|
child: ElevatedButton.icon(
|
||||||
const SizedBox(height: 10),
|
onPressed: submitForm,
|
||||||
LinearProgressIndicator(value: uploadProgress.value),
|
icon: const Icon(Icons.upload),
|
||||||
const SizedBox(height: 5),
|
label: const Text('发布'),
|
||||||
Text('${(uploadProgress.value * 100).toStringAsFixed(1)}%'),
|
),
|
||||||
],
|
),
|
||||||
)
|
|
||||||
: SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
child: ElevatedButton.icon(
|
|
||||||
onPressed: uploadVideo,
|
|
||||||
icon: const Icon(Icons.upload),
|
|
||||||
label: const Text('上传'),
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
Obx(() => Text(status.value)),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -21,6 +21,7 @@ import 'package:media_kit/media_kit.dart';
|
|||||||
import 'package:media_kit_video/media_kit_video.dart';
|
import 'package:media_kit_video/media_kit_video.dart';
|
||||||
import 'package:media_kit_video/media_kit_video_controls/src/controls/extensions/duration.dart';
|
import 'package:media_kit_video/media_kit_video_controls/src/controls/extensions/duration.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
||||||
|
|
||||||
import '../../../router/fade_route.dart';
|
import '../../../router/fade_route.dart';
|
||||||
import './components/popup_reply.dart';
|
import './components/popup_reply.dart';
|
||||||
|
|
||||||
@ -100,7 +101,7 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final res = await Http.get('${VideoApi.videoDetailApi}${videoId}');
|
final res = await Http.get('${VideoApi.videoDetailApi}$videoId');
|
||||||
|
|
||||||
logger.d('视频详情接口返回: ${json.encode(res)}');
|
logger.d('视频详情接口返回: ${json.encode(res)}');
|
||||||
|
|
||||||
@ -256,7 +257,6 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
|
|||||||
Future<void> unfollowUser() async {
|
Future<void> unfollowUser() async {
|
||||||
try {
|
try {
|
||||||
final vlogerId = videoData['vlogerId'] ?? videoData['memberId'];
|
final vlogerId = videoData['vlogerId'] ?? videoData['memberId'];
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.e('取消关注用户异常: $e');
|
logger.e('取消关注用户异常: $e');
|
||||||
MyToast().tip(
|
MyToast().tip(
|
||||||
@ -309,6 +309,8 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
|
|||||||
context: context,
|
context: context,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
|
final filteredList = chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).toList();
|
||||||
|
|
||||||
return Material(
|
return Material(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@ -348,15 +350,14 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
|
|||||||
),
|
),
|
||||||
|
|
||||||
// 会话列表
|
// 会话列表
|
||||||
if (chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).isNotEmpty)
|
if (filteredList.isNotEmpty)
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 110,
|
height: 110,
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
itemCount: chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).length,
|
itemCount: filteredList.length,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 20.0),
|
padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 20.0),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final filteredList = chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).toList();
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => handlCoverClick(filteredList[index].conversation),
|
onTap: () => handlCoverClick(filteredList[index].conversation),
|
||||||
child: Container(
|
child: Container(
|
||||||
@ -365,10 +366,12 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
NetworkOrAssetImage(
|
ClipOval(
|
||||||
imageUrl: filteredList[index].faceUrl,
|
child: NetworkOrAssetImage(
|
||||||
width: 48.0,
|
imageUrl: filteredList[index].faceUrl,
|
||||||
height: 48.0,
|
width: 48.0,
|
||||||
|
height: 48.0,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 5),
|
const SizedBox(height: 5),
|
||||||
Text(
|
Text(
|
||||||
@ -908,6 +911,7 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CommentBottomSheet extends StatefulWidget {
|
class CommentBottomSheet extends StatefulWidget {
|
||||||
final String videoId;
|
final String videoId;
|
||||||
final Function(int) onCommentCountChanged; // 新增回调函数
|
final Function(int) onCommentCountChanged; // 新增回调函数
|
||||||
@ -1401,4 +1405,4 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -798,6 +798,8 @@ class _AttentionModuleState extends State<AttentionModule> {
|
|||||||
context: context,
|
context: context,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
|
final filteredList = chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).toList();
|
||||||
|
|
||||||
return Material(
|
return Material(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@ -837,15 +839,14 @@ class _AttentionModuleState extends State<AttentionModule> {
|
|||||||
),
|
),
|
||||||
|
|
||||||
// 会话列表
|
// 会话列表
|
||||||
if (chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).isNotEmpty)
|
if (filteredList.isNotEmpty)
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 110,
|
height: 110,
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
itemCount: chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).length,
|
itemCount: filteredList.length,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 20.0),
|
padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 20.0),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final filteredList = chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).toList();
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => handlCoverClick(filteredList[index].conversation),
|
onTap: () => handlCoverClick(filteredList[index].conversation),
|
||||||
child: Container(
|
child: Container(
|
||||||
@ -1265,17 +1266,14 @@ class _AttentionModuleState extends State<AttentionModule> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
onTap: ()async {
|
onTap: () async {
|
||||||
player.pause();
|
player.pause();
|
||||||
// 跳转到举报页面并等待返回结果
|
// 跳转到举报页面并等待返回结果
|
||||||
final result = await Get.toNamed(
|
final result = await Get.toNamed('/report', arguments: videoList[videoModuleController.videoPlayIndex.value]);
|
||||||
'/report',
|
if (result != null) {
|
||||||
arguments: videoList[videoModuleController
|
player.play();
|
||||||
.videoPlayIndex.value]);
|
}
|
||||||
if (result != null) {
|
},
|
||||||
player.play();
|
|
||||||
};
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -15,6 +15,7 @@ import 'package:loopin/IM/im_service.dart' hide logger;
|
|||||||
import 'package:loopin/api/video_api.dart';
|
import 'package:loopin/api/video_api.dart';
|
||||||
import 'package:loopin/components/my_toast.dart';
|
import 'package:loopin/components/my_toast.dart';
|
||||||
import 'package:loopin/components/network_or_asset_image.dart';
|
import 'package:loopin/components/network_or_asset_image.dart';
|
||||||
|
import 'package:loopin/models/conversation_type.dart';
|
||||||
import 'package:loopin/models/summary_type.dart';
|
import 'package:loopin/models/summary_type.dart';
|
||||||
import 'package:loopin/service/http.dart';
|
import 'package:loopin/service/http.dart';
|
||||||
import 'package:loopin/utils/download_video.dart';
|
import 'package:loopin/utils/download_video.dart';
|
||||||
@ -794,6 +795,7 @@ class _RecommendModuleState extends State<RecommendModule> {
|
|||||||
context: context,
|
context: context,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
|
final filteredList = chatController.chatList.where((item) => conversationTypeFromString(item.isCustomAdmin) == null).toList();
|
||||||
return Material(
|
return Material(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@ -831,17 +833,15 @@ class _RecommendModuleState extends State<RecommendModule> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// 会话列表
|
// 会话列表
|
||||||
if (chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).isNotEmpty)
|
if (filteredList.isNotEmpty)
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 110,
|
height: 110,
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
itemCount: chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).length,
|
itemCount: filteredList.length,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 20.0),
|
padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 20.0),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final filteredList = chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).toList();
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => handlCoverClick(filteredList[index].conversation),
|
onTap: () => handlCoverClick(filteredList[index].conversation),
|
||||||
child: Container(
|
child: Container(
|
||||||
@ -850,10 +850,12 @@ class _RecommendModuleState extends State<RecommendModule> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
NetworkOrAssetImage(
|
ClipOval(
|
||||||
imageUrl: filteredList[index].faceUrl,
|
child: NetworkOrAssetImage(
|
||||||
width: 48.0,
|
imageUrl: filteredList[index].faceUrl,
|
||||||
height: 48.0,
|
width: 48.0,
|
||||||
|
height: 48.0,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 5),
|
const SizedBox(height: 5),
|
||||||
Text(
|
Text(
|
||||||
|
@ -11,5 +11,5 @@ Future<String?> generateVideoThumbnail(String videoPath) async {
|
|||||||
maxWidth: 120,
|
maxWidth: 120,
|
||||||
quality: 75,
|
quality: 75,
|
||||||
);
|
);
|
||||||
return thumbnailPath;
|
return thumbnailPath ?? '';
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user