diff --git a/lib/components/image_viewer.dart b/lib/components/image_viewer.dart index 52b8802..b28b056 100644 --- a/lib/components/image_viewer.dart +++ b/lib/components/image_viewer.dart @@ -1,6 +1,8 @@ /// 单图/多图预览查看器 library; +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:photo_view/photo_view.dart'; @@ -31,6 +33,19 @@ class _ImageViewerState extends State { 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 Widget build(BuildContext context) { var imgCount = widget.images?.length; @@ -44,39 +59,40 @@ class _ImageViewerState extends State { bottom: 0, right: 0, child: GestureDetector( - child: imgCount == 1 ? PhotoView( - imageProvider: Utils.isUrl(widget.images![0]) ? NetworkImage(widget.images![0]) : AssetImage(widget.images![0]), - backgroundDecoration: const BoxDecoration( - color: Colors.black, - ), - minScale: PhotoViewComputedScale.contained, - maxScale: PhotoViewComputedScale.covered * 2, - heroAttributes: PhotoViewHeroAttributes(tag: widget.images![0]), - enableRotation: true, - ) - : - PhotoViewGallery.builder( - itemCount: widget.images?.length, - builder: (context, index) { - return PhotoViewGalleryPageOptions( - imageProvider: Utils.isUrl(widget.images![index]) ? NetworkImage(widget.images![index]) : AssetImage(widget.images![index]), - minScale: PhotoViewComputedScale.contained, - maxScale: PhotoViewComputedScale.covered * 2, - heroAttributes: PhotoViewHeroAttributes(tag: widget.images![index]), - ); - }, - scrollPhysics: const BouncingScrollPhysics(), - backgroundDecoration: const BoxDecoration( - color: Colors.black, - ), - pageController: PageController(initialPage: widget.index), - enableRotation: true, - onPageChanged: (index) { - setState(() { - currentIndex = index; - }); - }, - ), + child: imgCount == 1 + ? PhotoView( + // imageProvider: Utils.isUrl(widget.images![0]) ? NetworkImage(widget.images![0]) : AssetImage(widget.images![0]), + imageProvider: getImageProvider(widget.images![0]), + backgroundDecoration: const BoxDecoration( + color: Colors.black, + ), + minScale: PhotoViewComputedScale.contained, + maxScale: PhotoViewComputedScale.covered * 2, + heroAttributes: PhotoViewHeroAttributes(tag: widget.images![0]), + enableRotation: true, + ) + : PhotoViewGallery.builder( + itemCount: widget.images?.length, + builder: (context, index) { + return PhotoViewGalleryPageOptions( + imageProvider: Utils.isUrl(widget.images![index]) ? NetworkImage(widget.images![index]) : AssetImage(widget.images![index]), + minScale: PhotoViewComputedScale.contained, + maxScale: PhotoViewComputedScale.covered * 2, + heroAttributes: PhotoViewHeroAttributes(tag: widget.images![index]), + ); + }, + scrollPhysics: const BouncingScrollPhysics(), + backgroundDecoration: const BoxDecoration( + color: Colors.black, + ), + pageController: PageController(initialPage: widget.index), + enableRotation: true, + onPageChanged: (index) { + setState(() { + currentIndex = index; + }); + }, + ), onTap: () { Get.back(); }, @@ -87,11 +103,13 @@ class _ImageViewerState extends State { top: MediaQuery.of(context).padding.top + 15, width: MediaQuery.of(context).size.width, child: Center( - child: Visibility( - visible: imgCount! > 1 ? true : false, - child: Text('${currentIndex+1} / ${widget.images?.length}', style: const TextStyle(color: Colors.white, fontSize: 16, fontFamily: 'arial'),), - ) - ), + child: Visibility( + visible: imgCount! > 1 ? true : false, + child: Text( + '${currentIndex + 1} / ${widget.images?.length}', + style: const TextStyle(color: Colors.white, fontSize: 16, fontFamily: 'arial'), + ), + )), ), ], ), diff --git a/lib/models/conversation_type.dart b/lib/models/conversation_type.dart index ae4e543..30b01dd 100644 --- a/lib/models/conversation_type.dart +++ b/lib/models/conversation_type.dart @@ -52,7 +52,7 @@ conversationTypeFromString(String? type) { return ConversationType.order.name; } else if (type.contains('groupNotify')) { return ConversationType.groupNotify.name; + } else { + return null; } - - return null; } diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 4d60796..b6a1b93 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -527,6 +527,7 @@ class _ChatState extends State with SingleTickerProviderStateMixin { else if (item.elemType == 2 && item.cloudCustomData == SummaryType.shareTuangou) { //price,title,url,sell final obj = jsonDecode(item.customElem!.data!); + logger.e(obj); final url = obj['url']; final title = obj['title']; final price = obj['price']; @@ -607,6 +608,7 @@ class _ChatState extends State with SingleTickerProviderStateMixin { else if (item.elemType == 2 && item.cloudCustomData == SummaryType.shareVideo) { /// {imgUrl,videoUrl,width,height} final obj = jsonDecode(item.customElem!.data!); + logger.e(obj); final videoUrl = obj['videoUrl']; final imgUrl = obj['imgUrl']; final width = obj['width'] as num; diff --git a/lib/pages/goods/detail.dart b/lib/pages/goods/detail.dart index 73b81d4..bba110a 100644 --- a/lib/pages/goods/detail.dart +++ b/lib/pages/goods/detail.dart @@ -13,6 +13,7 @@ import 'package:loopin/IM/im_service.dart'; import 'package:loopin/api/shop_api.dart'; import 'package:loopin/components/my_toast.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/summary_type.dart'; import 'package:loopin/service/http.dart'; @@ -48,7 +49,7 @@ class _GoodsState extends State { @override void initState() { super.initState(); - final goodsId = Get.arguments['goodsId']; + final goodsId = Get.arguments['goodsId'] ?? ''; scrollController.addListener(() { setState(() { scrollOffset = scrollController.offset; @@ -78,25 +79,22 @@ class _GoodsState extends State { } ///创建定点杆 - createOrder(String goodsId) async { - var params ={ - "type": 1, // 订单类型:1->团购;2->拼团;3->秒杀; - "distribution": 1, // 配送方式 1->到店核销;2->自提;3->配送; - "skuItemBOList": [ - { - "skuId": goodsId, - "quantity": 1 - } - ] - }; - print('下单请求参数---->${params}'); + createOrder(String goodsId) async { + var params = { + "type": 1, // 订单类型:1->团购;2->拼团;3->秒杀; + "distribution": 1, // 配送方式 1->到店核销;2->自提;3->配送; + "skuItemBOList": [ + {"skuId": goodsId, "quantity": 1} + ] + }; + print('下单请求参数---->$params'); try { - final res = await Http.post('${ShopApi.createGoodsOrder}', data: params); + final res = await Http.post(ShopApi.createGoodsOrder, data: params); var resData = res['data']; - print('1111111111111111111111111---->${res}'); - if(resData['id'].isNotEmpty){ + print('1111111111111111111111111---->$res'); + if (resData['id'].isNotEmpty) { return resData['id']; - }else{ + } else { return null; } } catch (e) { @@ -104,6 +102,7 @@ class _GoodsState extends State { return null; } } + void handleShareClick(int index) { final description = shopObj['describe']; // 商品描述 if (index == 1) { @@ -116,12 +115,13 @@ class _GoodsState extends State { } void handlCoverClick(V2TimConversation conv) async { - // 发送VideoMsg,获取当前视频信息 + // 发送自定义消息 商品信息 final userId = conv.userID; //price,title,url,sell + logger.w(shopObj['name']); final makeJson = jsonEncode({ "price": shopObj['price'], - "title": shopObj['describe'], + "title": shopObj['name'], "url": shopObj['pic'], "sell": Utils.graceNumber(int.parse(shopObj['sales'] ?? '0')), }); @@ -200,7 +200,8 @@ class _GoodsState extends State { // 会话列表 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(); return SizedBox( height: 110, @@ -219,10 +220,12 @@ class _GoodsState extends State { mainAxisSize: MainAxisSize.min, children: [ // Image.asset('${chatController.chatList[index].faceUrl}', width: 48.0), - NetworkOrAssetImage( - imageUrl: filteredList[index].faceUrl, - width: 48.0, - height: 48.0, + ClipOval( + child: NetworkOrAssetImage( + imageUrl: filteredList[index].faceUrl, + width: 48.0, + height: 48.0, + ), ), SizedBox(height: 5), Text( @@ -338,7 +341,12 @@ class _GoodsState extends State { activeColor: Colors.white, )), 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 { // ) // ], // ), - GestureDetector( - onTap: () async { - // 可以在这里打开聊天 - logger.i('联系客服'); - final res = await ImService.instance.getConversation(conversationID: 'c2c_${shopObj['tenantId']}'); - V2TimConversation conversation = res.data; - logger.i(conversation.toLogString()); - if (res.success) { - // 客服聊天不用检测关注关系 - conversation.showName = conversation.showName ?? shopObj['storeName']; - Get.toNamed('/chat', arguments: conversation); - } else { - MyDialog.toast(res.desc, icon: const Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200))); - } - }, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.child_care_outlined, - size: 18.0, - ), - Text( - '联系商家', - style: TextStyle(fontSize: 12.0), - ) - ], - ), - ) + + // 联系商家 + // GestureDetector( + // onTap: () async { + // // 可以在这里打开聊天 + // logger.i('联系客服:$shopObj'); + // final res = await ImService.instance.getConversation(conversationID: 'c2c_${shopObj['tenantId']}'); + // V2TimConversation conversation = res.data; + // logger.i(conversation.toLogString()); + // if (res.success) { + // // 客服聊天不用检测关注关系 + // V2TimUserFullInfo? sellerInfo; + // final resIm = await ImService.instance.otherInfo(shopObj['tenantId']); + // if (resIm.success && resIm.data != null) { + // sellerInfo = resIm.data!; + // logger.i(sellerInfo!.toLogString()); + // } else { + // logger.e(resIm.desc); + // } + // conversation.showName = conversation.showName ?? sellerInfo!.nickName; + // Get.toNamed('/chat', arguments: conversation); + // } else { + // MyDialog.toast(res.desc, icon: const Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200))); + // } + // }, + // child: Column( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // Icon( + // Icons.child_care_outlined, + // size: 18.0, + // ), + // Text( + // '联系商家', + // style: TextStyle(fontSize: 12.0), + // ) + // ], + // ), + // ) + // Column( // mainAxisAlignment: MainAxisAlignment.center, // children: [ @@ -650,11 +669,11 @@ class _GoodsState extends State { child: GestureDetector( onTap: () async { // 这里走生成预支付订单,拿到orderId - // String orderId = '1958380183857659904'; //测试数据 + // String orderId = '1958380183857659904'; //测试数据 String orderId = await createOrder(shopObj['skuList'][0]['id']); - if(orderId.isNotEmpty){ - Get.toNamed('/order/detail', arguments: orderId); - }else{ + if (orderId.isNotEmpty) { + Get.toNamed('/order/detail', arguments: orderId); + } else { MyDialog.toast('生成订单失败', icon: const Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200))); } }, diff --git a/lib/pages/my/fans.dart b/lib/pages/my/fans.dart index a3b6acf..2e35c89 100644 --- a/lib/pages/my/fans.dart +++ b/lib/pages/my/fans.dart @@ -182,7 +182,7 @@ class FansState extends State with SingleTickerProviderStateMixin { onTap: () { Get.toNamed( '/vloger', - arguments: item.userInfo.userID, + arguments: {'memberId': item.userInfo.userID}, ); }, child: Row( diff --git a/lib/pages/my/flowing.dart b/lib/pages/my/flowing.dart index 59ced48..ce6414f 100644 --- a/lib/pages/my/flowing.dart +++ b/lib/pages/my/flowing.dart @@ -182,7 +182,7 @@ class FlowingState extends State with SingleTickerProviderStateMixin { onTap: () { Get.toNamed( '/vloger', - arguments: item.userInfo.userID, + arguments: {'memberId': item.userInfo.userID}, ); }, child: Row( diff --git a/lib/pages/my/mutual_followers.dart b/lib/pages/my/mutual_followers.dart index b3ede42..c8efc0b 100644 --- a/lib/pages/my/mutual_followers.dart +++ b/lib/pages/my/mutual_followers.dart @@ -182,7 +182,7 @@ class MutualFollowersState extends State with SingleTickerProvi onTap: () { Get.toNamed( '/vloger', - arguments: item.userInfo.userID, + arguments: {'memberId': item.userInfo.userID}, ); }, child: Row( diff --git a/lib/pages/upload_video_page/upload_video_page.dart b/lib/pages/upload_video_page/upload_video_page.dart index 13f9a62..1edc5d3 100644 --- a/lib/pages/upload_video_page/upload_video_page.dart +++ b/lib/pages/upload_video_page/upload_video_page.dart @@ -1,12 +1,15 @@ import 'dart:io'; -import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:loopin/IM/im_service.dart'; import 'package:loopin/api/common_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/utils/index.dart'; +import 'package:loopin/utils/snapshot.dart'; import 'package:wechat_assets_picker/wechat_assets_picker.dart'; class UploadVideoPage extends StatefulWidget { @@ -19,15 +22,28 @@ class UploadVideoPage extends StatefulWidget { class _UploadVideoPageState extends State { final selectedVideo = Rxn(); final selectedCover = Rxn(); + //视频 final uploading = false.obs; final uploadProgress = 0.0.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 uploadedVideoUrl = ''.obs; + final TextEditingController descriptionController = TextEditingController(); Future pickVideo() async { + FocusScope.of(context).unfocus(); + final result = await PhotoManager.requestPermissionExtend(); if (!result.isAuth) { @@ -60,10 +76,14 @@ class _UploadVideoPageState extends State { if (pickedAssets != null && pickedAssets.isNotEmpty) { selectedVideo.value = pickedAssets.first; status.value = '已选择视频'; + uploadVideo(); } } + // 选择封面图 Future pickCoverImage() async { + FocusScope.of(context).unfocus(); + final result = await PhotoManager.requestPermissionExtend(); if (!result.isAuth) { @@ -88,30 +108,73 @@ class _UploadVideoPageState extends State { if (pickedAssets != null && pickedAssets.isNotEmpty) { selectedCover.value = pickedAssets.first; + // 执行上传图片逻辑 + uploadImg(); } } + // 上传图片 + Future 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 uploadVideo() async { final file = await selectedVideo.value?.file; - final coverFile = await selectedCover.value?.file; - final desc = descController.text.trim(); if (file == null) { status.value = '未选择视频'; return; } - if (desc.isEmpty) { - Get.snackbar('请填写描述', '视频描述不能为空'); - return; - } - uploading.value = true; uploadProgress.value = 0; status.value = '上传中...'; + videoPath.value = file.path; + logger.w(videoPath.value); + + snapshot.value = (await generateVideoThumbnail(file.path))!; + logger.w(snapshot.value); try { - final data = await Http.upload( + final res = await Http.upload( CommonApi.uploadFile, filePath: file.path, fileKey: 'file', @@ -121,60 +184,98 @@ class _UploadVideoPageState extends State { } }, ); - uploadedVideoUrl.value = data; + uploadedVideoUrl.value = res['data']['url']; status.value = '上传成功'; - // 清空表单 - descController.clear(); - selectedVideo.value = null; - selectedCover.value = null; - uploadedVideoUrl.value = ''; } catch (e) { + descFocusNode.unfocus(); if (e is SocketException) { status.value = '网络错误,请检查连接'; } else { status.value = '上传失败: ${e.toString()}'; } } finally { + descFocusNode.unfocus(); uploading.value = false; } } + // 发布 Future submitForm() async { + logger.w(descriptionController.text); + + FocusScope.of(context).unfocus(); if (uploadedVideoUrl.value.isEmpty) { Get.snackbar('请先上传视频', '未检测到上传的视频'); return; } - if (descriptionController.text.trim().isEmpty) { Get.snackbar('请填写视频描述', '描述是必填项'); return; } try { - await Http.post(VideoApi.publish, data: { - 'video_url': uploadedVideoUrl.value, - 'desc': descriptionController.text.trim(), - 'cover': selectedCover.value, - }); - Get.snackbar('发布成功', '视频已成功发布'); + final data = { + 'url': uploadedVideoUrl.value, + 'title': descriptionController.text.trim(), + }; + if (uploadedImgUrl.value.isNotEmpty) { + data['cover'] = uploadedImgUrl.value; + } + logger.w('发布内容:$data'); + await Http.post(VideoApi.publish, data: data); + Get.snackbar('发布成功', '视频正在审核中'); clearForm(); } catch (e) { Get.snackbar('发布失败', '$e'); - } + } finally {} } void clearForm() { selectedVideo.value = null; selectedCover.value = null; - descriptionController.clear(); - status.value = ''; - uploadProgress.value = 0.0; + uploadedImgUrl.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 void dispose() { - descController.dispose(); + descriptionController.dispose(); descFocusNode.dispose(); super.dispose(); } @@ -193,7 +294,8 @@ class _UploadVideoPageState extends State { children: [ TextField( focusNode: descFocusNode, - controller: descController, + autofocus: false, + controller: descriptionController, decoration: const InputDecoration( labelText: '视频描述 *', border: OutlineInputBorder(), @@ -201,72 +303,125 @@ class _UploadVideoPageState extends State { maxLines: 2, ), const SizedBox(height: 20), + // 视频 Obx(() { - final video = selectedVideo.value; return Row( + crossAxisAlignment: CrossAxisAlignment.center, 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( - child: Text( - video != null ? '已选择视频:${video.title}' : '未选择视频', - style: const TextStyle(fontSize: 16), - ), - ), - ElevatedButton( - onPressed: pickVideo, - child: const Text('选择视频'), + child: uploading.value + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const CircularProgressIndicator(), + const SizedBox(height: 10), + LinearProgressIndicator(value: uploadProgress.value), + 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), + // 图片上传 Obx(() { - final cover = selectedCover.value; return Row( + crossAxisAlignment: CrossAxisAlignment.center, children: [ - cover != null - ? FutureBuilder( - future: cover.thumbnailDataWithSize(const ThumbnailSize(80, 80)), - builder: (_, snapshot) { - if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) { - return ClipRRect( - borderRadius: BorderRadius.circular(8), - child: Image.memory(snapshot.data!, width: 80, height: 80), - ); - } else { - return const SizedBox(width: 80, height: 80); - } - }, + // 左侧图片预览 + imgPath.value.isNotEmpty + ? GestureDetector( + onTap: () => openImg(), + child: ClipRRect( + borderRadius: BorderRadius.circular(8), + child: Image.file( + File(imgPath.value), + width: 80, + fit: BoxFit.cover, + ), + ), ) - : const Text('未选择封面图'), - const SizedBox(width: 12), - ElevatedButton( - onPressed: pickCoverImage, - child: const Text('选择封面(可选)'), + : const SizedBox( + height: 80, + child: Center( + child: 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), - Obx(() => uploading.value - ? Column( - children: [ - const CircularProgressIndicator(), - const SizedBox(height: 10), - LinearProgressIndicator(value: uploadProgress.value), - const SizedBox(height: 5), - 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)), + // 发布按钮 + SizedBox( + width: double.infinity, + child: ElevatedButton.icon( + onPressed: submitForm, + icon: const Icon(Icons.upload), + label: const Text('发布'), + ), + ), ], ), ), diff --git a/lib/pages/video/commonVideo.dart b/lib/pages/video/commonVideo.dart index 6215768..17f47a3 100644 --- a/lib/pages/video/commonVideo.dart +++ b/lib/pages/video/commonVideo.dart @@ -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_controls/src/controls/extensions/duration.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart'; + import '../../../router/fade_route.dart'; import './components/popup_reply.dart'; @@ -100,7 +101,7 @@ class _VideoDetailPageState extends State { }); try { - final res = await Http.get('${VideoApi.videoDetailApi}${videoId}'); + final res = await Http.get('${VideoApi.videoDetailApi}$videoId'); logger.d('视频详情接口返回: ${json.encode(res)}'); @@ -256,7 +257,6 @@ class _VideoDetailPageState extends State { Future unfollowUser() async { try { final vlogerId = videoData['vlogerId'] ?? videoData['memberId']; - } catch (e) { logger.e('取消关注用户异常: $e'); MyToast().tip( @@ -309,6 +309,8 @@ class _VideoDetailPageState extends State { context: context, isScrollControlled: true, builder: (context) { + final filteredList = chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).toList(); + return Material( color: Colors.white, child: Padding( @@ -348,15 +350,14 @@ class _VideoDetailPageState extends State { ), // 会话列表 - if (chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).isNotEmpty) + if (filteredList.isNotEmpty) SizedBox( height: 110, child: ListView.builder( 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), itemBuilder: (context, index) { - final filteredList = chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).toList(); return GestureDetector( onTap: () => handlCoverClick(filteredList[index].conversation), child: Container( @@ -365,10 +366,12 @@ class _VideoDetailPageState extends State { child: Column( mainAxisSize: MainAxisSize.min, children: [ - NetworkOrAssetImage( - imageUrl: filteredList[index].faceUrl, - width: 48.0, - height: 48.0, + ClipOval( + child: NetworkOrAssetImage( + imageUrl: filteredList[index].faceUrl, + width: 48.0, + height: 48.0, + ), ), const SizedBox(height: 5), Text( @@ -908,6 +911,7 @@ class _VideoDetailPageState extends State { ); } } + class CommentBottomSheet extends StatefulWidget { final String videoId; final Function(int) onCommentCountChanged; // 新增回调函数 @@ -1401,4 +1405,4 @@ class _CommentBottomSheetState extends State { ), ); } -} \ No newline at end of file +} diff --git a/lib/pages/video/module/attention.dart b/lib/pages/video/module/attention.dart index ac48c8f..85474de 100644 --- a/lib/pages/video/module/attention.dart +++ b/lib/pages/video/module/attention.dart @@ -794,6 +794,8 @@ class _AttentionModuleState extends State { context: context, isScrollControlled: true, builder: (context) { + final filteredList = chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).toList(); + return Material( color: Colors.white, child: Padding( @@ -833,15 +835,14 @@ class _AttentionModuleState extends State { ), // 会话列表 - if (chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).isNotEmpty) + if (filteredList.isNotEmpty) SizedBox( height: 110, child: ListView.builder( 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), itemBuilder: (context, index) { - final filteredList = chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).toList(); return GestureDetector( onTap: () => handlCoverClick(filteredList[index].conversation), child: Container( @@ -1247,17 +1248,14 @@ class _AttentionModuleState extends State { ), ], ), - onTap: ()async { - player.pause(); - // 跳转到举报页面并等待返回结果 - final result = await Get.toNamed( - '/report', - arguments: videoList[videoModuleController - .videoPlayIndex.value]); - if (result != null) { - player.play(); - }; - }, + onTap: () async { + player.pause(); + // 跳转到举报页面并等待返回结果 + final result = await Get.toNamed('/report', arguments: videoList[videoModuleController.videoPlayIndex.value]); + if (result != null) { + player.play(); + } + }, ), ], ), diff --git a/lib/pages/video/module/recommend.dart b/lib/pages/video/module/recommend.dart index 0b2f1c9..e838d5d 100644 --- a/lib/pages/video/module/recommend.dart +++ b/lib/pages/video/module/recommend.dart @@ -15,6 +15,7 @@ import 'package:loopin/IM/im_service.dart' hide logger; import 'package:loopin/api/video_api.dart'; import 'package:loopin/components/my_toast.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/service/http.dart'; import 'package:loopin/utils/download_video.dart'; @@ -794,6 +795,7 @@ class _RecommendModuleState extends State { context: context, isScrollControlled: true, builder: (context) { + final filteredList = chatController.chatList.where((item) => conversationTypeFromString(item.isCustomAdmin) == null).toList(); return Material( color: Colors.white, child: Padding( @@ -831,17 +833,15 @@ class _RecommendModuleState extends State { }, ), ), - // 会话列表 - if (chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).isNotEmpty) + if (filteredList.isNotEmpty) SizedBox( height: 110, child: ListView.builder( 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), itemBuilder: (context, index) { - final filteredList = chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).toList(); return GestureDetector( onTap: () => handlCoverClick(filteredList[index].conversation), child: Container( @@ -850,10 +850,12 @@ class _RecommendModuleState extends State { child: Column( mainAxisSize: MainAxisSize.min, children: [ - NetworkOrAssetImage( - imageUrl: filteredList[index].faceUrl, - width: 48.0, - height: 48.0, + ClipOval( + child: NetworkOrAssetImage( + imageUrl: filteredList[index].faceUrl, + width: 48.0, + height: 48.0, + ), ), const SizedBox(height: 5), Text( diff --git a/lib/utils/snapshot.dart b/lib/utils/snapshot.dart index 1be4149..0d54b7f 100644 --- a/lib/utils/snapshot.dart +++ b/lib/utils/snapshot.dart @@ -11,5 +11,5 @@ Future generateVideoThumbnail(String videoPath) async { maxWidth: 120, quality: 75, ); - return thumbnailPath; + return thumbnailPath ?? ''; }