merge
This commit is contained in:
parent
4f0c1f6f73
commit
2ba610ca7f
1
assets/animation/voice.json
Normal file
1
assets/animation/voice.json
Normal file
File diff suppressed because one or more lines are too long
BIN
assets/images/icon_logout.png
Normal file
BIN
assets/images/icon_logout.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
@ -23,7 +23,6 @@ class IMMessage {
|
||||
String? groupID,
|
||||
String? cloudCustomData,
|
||||
String? groupName,
|
||||
bool isPush = true,
|
||||
bool isExcludedFromUnreadCount = false,
|
||||
}) async {
|
||||
// 必须且只能设置一个:toUserID(单聊)或 groupID(群聊)
|
||||
@ -67,7 +66,7 @@ class IMMessage {
|
||||
isSupportMessageExtension: false, // 支持消息扩展
|
||||
isExcludedFromContentModeration: false, // 绕过内容审核
|
||||
needReadReceipt: false, // 已读回执
|
||||
offlinePushInfo: isPush ? offlinePushInfo : null,
|
||||
offlinePushInfo: offlinePushInfo,
|
||||
cloudCustomData: cloudCustomData,
|
||||
localCustomData: "",
|
||||
);
|
||||
@ -96,7 +95,7 @@ class IMMessage {
|
||||
isSupportMessageExtension: false,
|
||||
isExcludedFromContentModeration: false,
|
||||
needReadReceipt: false,
|
||||
offlinePushInfo: isPush ? offlinePushInfo : null,
|
||||
offlinePushInfo: offlinePushInfo,
|
||||
cloudCustomData: cloudCustomData,
|
||||
localCustomData: "",
|
||||
);
|
||||
|
@ -13,6 +13,7 @@ import 'package:shirne_dialog/shirne_dialog.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/V2TimAdvancedMsgListener.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/message_elem_type.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/receive_message_opt.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_receipt.dart';
|
||||
@ -123,6 +124,14 @@ class ImMessageListenerService extends GetxService {
|
||||
// 获取失败默认也不提示;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// 单聊的
|
||||
final cRes = await ImService.instance.getConversation(conversationID: 'c2c_${message.userID}');
|
||||
final V2TimConversation c2cConv = cRes.data;
|
||||
if (c2cConv.recvOpt == ReceiveMsgOptType.kTIMRecvMsgOpt_Not_Notify) {
|
||||
// 开启了免打扰模式, 现在只做了0=初始正常接受,3=在线接受,离线只接受@消息
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_debounceTimer?.cancel(); // 每次都取消旧的
|
||||
|
@ -1,6 +1,9 @@
|
||||
class CommonApi {
|
||||
///----------get
|
||||
static const String getCode = '/resource/sms/code'; // 发送短信验证码 {'phonenumber'}
|
||||
// /app/member/sms/code
|
||||
static const String getDelCode = '/app/member/sms/code'; // 发送短信验证码 {'templateId'}
|
||||
|
||||
static const String accountInfo = '/app/member/info'; // 账户信息
|
||||
|
||||
///---------post
|
||||
@ -11,11 +14,31 @@ class CommonApi {
|
||||
///[source]=wechat_open [clientId]=428a8310cd442757ae699df5d894f051 [grantType]=social [socialState]=1
|
||||
static const String wxLogin = '/app/member/bind/wechat';
|
||||
|
||||
// 获取字典枚举
|
||||
/// 获取字典枚举 用户注销[member_revoked]
|
||||
static const String dictionaryApi = '/app/sys/dict/type/';
|
||||
|
||||
// 聚合搜索
|
||||
static const String aggregationSearchApi = '/app/common/search';
|
||||
|
||||
///余额充值 [money]金额
|
||||
// {
|
||||
// "orderType": "RECHARGE",
|
||||
// "clientType": "APP",
|
||||
// "paymentMethod": "WECHAT",
|
||||
// "paymentClient": "APP",
|
||||
// "money": 100
|
||||
// }
|
||||
static const String addBalance = '/app/payment/pay';
|
||||
|
||||
///余额提现
|
||||
// {
|
||||
// "money": "1",
|
||||
// "method": "1",
|
||||
// }
|
||||
static const String withdraw = '/app/member/withdraw';
|
||||
|
||||
///注销 /app/member/revoked
|
||||
static const String revoked = '/app/member/revoked';
|
||||
|
||||
///resource/oss/upload
|
||||
}
|
||||
|
41
lib/components/animation.dart
Normal file
41
lib/components/animation.dart
Normal file
@ -0,0 +1,41 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lottie/lottie.dart';
|
||||
|
||||
class VoiceAnimation extends StatelessWidget {
|
||||
/// 是否正在播放
|
||||
final bool isPlaying;
|
||||
|
||||
/// Lottie 动画资源路径
|
||||
final String animationAsset;
|
||||
|
||||
/// 静态图标
|
||||
final IconData idleIcon;
|
||||
|
||||
/// 动画和图标的大小
|
||||
final double size;
|
||||
|
||||
const VoiceAnimation({
|
||||
super.key,
|
||||
required this.isPlaying,
|
||||
this.animationAsset = 'assets/animation/voice.json',
|
||||
this.idleIcon = Icons.multitrack_audio, // 静态图标
|
||||
this.size = 24.0,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (isPlaying) {
|
||||
return Lottie.asset(
|
||||
animationAsset,
|
||||
width: size,
|
||||
height: size,
|
||||
repeat: true,
|
||||
);
|
||||
} else {
|
||||
return Icon(
|
||||
idleIcon,
|
||||
size: size,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -17,9 +17,11 @@ class MyQrcode extends StatelessWidget {
|
||||
MyQrcode({
|
||||
super.key,
|
||||
this.prefix,
|
||||
this.text,
|
||||
});
|
||||
|
||||
final String? prefix;
|
||||
final String? text;
|
||||
|
||||
final controller = Get.find<ImUserInfoController>();
|
||||
|
||||
@ -106,29 +108,45 @@ class MyQrcode extends StatelessWidget {
|
||||
child: Center(
|
||||
child: Container(
|
||||
alignment: Alignment.topCenter,
|
||||
decoration: BoxDecoration(
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
),
|
||||
child: RepaintBoundary(
|
||||
key: _qrKey,
|
||||
child: PrettyQrView.data(
|
||||
errorCorrectLevel: QrErrorCorrectLevel.H, // 高容错
|
||||
data: data, // 二维码内容
|
||||
decoration: PrettyQrDecoration(
|
||||
background: Colors.transparent,
|
||||
shape: const PrettyQrShape.custom(
|
||||
PrettyQrSmoothSymbol(color: FStyle.primaryColor),
|
||||
finderPattern: PrettyQrSmoothSymbol(color: FStyle.primaryColor),
|
||||
alignmentPatterns: PrettyQrSmoothSymbol(color: FStyle.primaryColor),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min, // 根据内容高度自适应
|
||||
children: [
|
||||
PrettyQrView.data(
|
||||
errorCorrectLevel: QrErrorCorrectLevel.H,
|
||||
data: data, // 二维码内容
|
||||
decoration: PrettyQrDecoration(
|
||||
background: Colors.transparent,
|
||||
shape: const PrettyQrShape.custom(
|
||||
PrettyQrSmoothSymbol(color: FStyle.primaryColor),
|
||||
finderPattern: PrettyQrSmoothSymbol(color: FStyle.primaryColor),
|
||||
alignmentPatterns: PrettyQrSmoothSymbol(color: FStyle.primaryColor),
|
||||
),
|
||||
image: PrettyQrDecorationImage(
|
||||
image: face,
|
||||
scale: 0.3,
|
||||
opacity: 1,
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
),
|
||||
quietZone: const PrettyQrQuietZone.modules(2),
|
||||
),
|
||||
),
|
||||
image: PrettyQrDecorationImage(
|
||||
image: face,
|
||||
scale: 0.3, // 默认
|
||||
opacity: 1,
|
||||
padding: EdgeInsets.all(8.0), // 图片与二维码的边距
|
||||
),
|
||||
quietZone: const PrettyQrQuietZone.modules(2),
|
||||
),
|
||||
if (text != null && text!.trim().isNotEmpty) ...[
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
text!,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -12,6 +12,7 @@ import 'package:loopin/IM/controller/chat_detail_controller.dart';
|
||||
import 'package:loopin/IM/im_message.dart';
|
||||
import 'package:loopin/IM/im_result.dart';
|
||||
import 'package:loopin/IM/im_service.dart';
|
||||
import 'package:loopin/components/animation.dart';
|
||||
import 'package:loopin/components/image_viewer.dart';
|
||||
import 'package:loopin/components/network_or_asset_image.dart';
|
||||
import 'package:loopin/components/preview_video.dart';
|
||||
@ -56,6 +57,8 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
|
||||
bool hasMore = true; // 是否还有更多数据
|
||||
final RxBool _throttleFlag = false.obs; // 滚动节流锁
|
||||
|
||||
RxMap<String, bool> voicePlayingMap = <String, bool>{}.obs;
|
||||
|
||||
// 表情json
|
||||
List emoJson = emotionData;
|
||||
|
||||
@ -498,32 +501,47 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
|
||||
maxWidth: maxWidth,
|
||||
// maxWidth: (item.soundElem!.duration! / 1000) / 60 * 230,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: !(item.isSelf ?? false) ? MainAxisAlignment.start : MainAxisAlignment.end,
|
||||
children: !(item.isSelf ?? false)
|
||||
? [
|
||||
const Icon(Icons.multitrack_audio),
|
||||
const SizedBox(
|
||||
width: 5.0,
|
||||
),
|
||||
Text('$durationSeconds"'),
|
||||
]
|
||||
: [
|
||||
Text('$durationSeconds"'),
|
||||
const SizedBox(
|
||||
width: 5.0,
|
||||
),
|
||||
const Icon(Icons.multitrack_audio),
|
||||
],
|
||||
child: Obx(
|
||||
() => Row(
|
||||
mainAxisAlignment: !(item.isSelf ?? false) ? MainAxisAlignment.start : MainAxisAlignment.end,
|
||||
children: !(item.isSelf ?? false)
|
||||
? [
|
||||
// const Icon(Icons.multitrack_audio),
|
||||
VoiceAnimation(
|
||||
isPlaying: voicePlayingMap[item.id ?? '${item.timestamp ?? 0}'] ?? false,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 5.0,
|
||||
),
|
||||
Text('$durationSeconds"'),
|
||||
]
|
||||
: [
|
||||
Text('$durationSeconds"'),
|
||||
const SizedBox(
|
||||
width: 5.0,
|
||||
),
|
||||
// const Icon(Icons.multitrack_audio),
|
||||
VoiceAnimation(
|
||||
isPlaying: voicePlayingMap[item.id ?? '${item.timestamp ?? 0}'] ?? false,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
onTap: () async {
|
||||
final locUrl = item.soundElem?.path ?? '';
|
||||
final netUrl = item.soundElem?.url ?? '';
|
||||
final msgId = item.id ?? '${item.timestamp ?? 0}';
|
||||
logger.w('本地地址$locUrl');
|
||||
logger.w('网络地址$netUrl');
|
||||
if (locUrl.isNotEmpty) {
|
||||
AudioPlayerService().playNetwork(locUrl);
|
||||
voicePlayingMap[msgId] = true;
|
||||
await AudioPlayerService().playLocal(locUrl);
|
||||
voicePlayingMap[msgId] = false;
|
||||
} else if (netUrl.isNotEmpty) {
|
||||
AudioPlayerService().playLocal(netUrl);
|
||||
voicePlayingMap[msgId] = true;
|
||||
await AudioPlayerService().playNetwork(netUrl);
|
||||
voicePlayingMap[msgId] = false;
|
||||
} else {
|
||||
MyDialog.toast('音频文件已过期', icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
||||
}
|
||||
@ -1907,6 +1925,7 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
|
||||
),
|
||||
onPanStart: (details) async {
|
||||
// 开始录音
|
||||
logger.w('开始录音');
|
||||
final res = await VoiceService().startRecording();
|
||||
if (res) {
|
||||
setState(() {
|
||||
@ -1932,7 +1951,7 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
|
||||
});
|
||||
},
|
||||
onPanEnd: (details) {
|
||||
// print('停止录音');
|
||||
logger.w('停止录音');
|
||||
setState(() {
|
||||
switch (voiceType) {
|
||||
case 1:
|
||||
|
@ -55,6 +55,7 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
||||
bool isLoading = false; // 是否在加载中
|
||||
bool hasMore = true; // 是否还有更多数据
|
||||
final RxBool _throttleFlag = false.obs; // 滚动节流锁
|
||||
RxMap<String, bool> voicePlayingMap = <String, bool>{}.obs;
|
||||
|
||||
// 表情json
|
||||
List emoJson = emotionData;
|
||||
@ -457,13 +458,20 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
||||
],
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
onTap: () async {
|
||||
final locUrl = item.soundElem?.path ?? '';
|
||||
final netUrl = item.soundElem?.url ?? '';
|
||||
final msgId = item.id ?? '${item.timestamp ?? 0}';
|
||||
logger.w('本地地址$locUrl');
|
||||
logger.w('网络地址$netUrl');
|
||||
if (locUrl.isNotEmpty) {
|
||||
AudioPlayerService().playNetwork(locUrl);
|
||||
voicePlayingMap[msgId] = true;
|
||||
await AudioPlayerService().playLocal(locUrl);
|
||||
voicePlayingMap[msgId] = false;
|
||||
} else if (netUrl.isNotEmpty) {
|
||||
AudioPlayerService().playLocal(netUrl);
|
||||
voicePlayingMap[msgId] = true;
|
||||
await AudioPlayerService().playNetwork(netUrl);
|
||||
voicePlayingMap[msgId] = false;
|
||||
} else {
|
||||
MyDialog.toast('音频文件已过期', icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
||||
}
|
||||
|
@ -53,6 +53,8 @@ class _ChatNoFriendState extends State<ChatNoFriend> with SingleTickerProviderSt
|
||||
bool hasMore = true; // 是否还有更多数据
|
||||
final RxBool _throttleFlag = false.obs; // 滚动节流锁
|
||||
|
||||
RxMap<String, bool> voicePlayingMap = <String, bool>{}.obs;
|
||||
|
||||
// 表情json
|
||||
List emoJson = emotionData;
|
||||
|
||||
@ -519,13 +521,20 @@ class _ChatNoFriendState extends State<ChatNoFriend> with SingleTickerProviderSt
|
||||
],
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
onTap: () async {
|
||||
final locUrl = item.soundElem?.path ?? '';
|
||||
final netUrl = item.soundElem?.url ?? '';
|
||||
final msgId = item.id ?? '${item.timestamp ?? 0}';
|
||||
logger.w('本地地址$locUrl');
|
||||
logger.w('网络地址$netUrl');
|
||||
if (locUrl.isNotEmpty) {
|
||||
AudioPlayerService().playNetwork(locUrl);
|
||||
voicePlayingMap[msgId] = true;
|
||||
await AudioPlayerService().playLocal(locUrl);
|
||||
voicePlayingMap[msgId] = false;
|
||||
} else if (netUrl.isNotEmpty) {
|
||||
AudioPlayerService().playLocal(netUrl);
|
||||
voicePlayingMap[msgId] = true;
|
||||
await AudioPlayerService().playNetwork(netUrl);
|
||||
voicePlayingMap[msgId] = false;
|
||||
} else {
|
||||
MyDialog.toast('音频文件已过期', icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
||||
}
|
||||
|
@ -9,8 +9,10 @@ import 'package:loopin/IM/controller/chat_controller.dart';
|
||||
import 'package:loopin/IM/global_badge.dart';
|
||||
import 'package:loopin/IM/im_service.dart';
|
||||
import 'package:loopin/api/shop_api.dart';
|
||||
import 'package:loopin/components/empty_tip.dart';
|
||||
import 'package:loopin/components/network_or_asset_image.dart';
|
||||
import 'package:loopin/components/scan_util.dart';
|
||||
import 'package:loopin/models/conversation_type.dart' as myConversationType;
|
||||
import 'package:loopin/models/conversation_type.dart';
|
||||
import 'package:loopin/models/conversation_view_model.dart';
|
||||
import 'package:loopin/pages/chat/menu/add_friend.dart';
|
||||
@ -20,6 +22,8 @@ import 'package:loopin/utils/parse_message_summary.dart';
|
||||
import 'package:loopin/utils/scan_code_type.dart'; // 导入外部枚举
|
||||
import 'package:shirne_dialog/shirne_dialog.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/receive_message_opt.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/receive_message_opt_enum.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation_filter.dart';
|
||||
|
||||
import '../../behavior/custom_scroll_behavior.dart';
|
||||
import '../../styles/index.dart';
|
||||
@ -103,8 +107,8 @@ class ChatPageState extends State<ChatPage> {
|
||||
Get.toNamed('/vloger', arguments: {'memberId': value});
|
||||
}
|
||||
|
||||
// 检测当前用户是否关注博主
|
||||
checkFollowType(memberId) async {
|
||||
// 检测当前用户是否关注博主
|
||||
checkFollowType(memberId) async {
|
||||
/// 0:不是好友也没有关注
|
||||
/// 1:你关注了对方(单向)
|
||||
/// 2:对方关注了你(单向)
|
||||
@ -118,22 +122,21 @@ class ChatPageState extends State<ChatPage> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 处理推广码,先调用IM互相关注逻辑,成功后在调用绑定关系接口
|
||||
void _handlePromotionCode(String value) async {
|
||||
await checkFollowType(value); // 检查当前用户是否关注了团长
|
||||
if(followed.value == 0 || followed.value == 2){
|
||||
final res = await ImService.instance.followUser(userIDList: [value]);
|
||||
if (res.success) {
|
||||
followed.value = followed.value == 0 ? 1 : 3;
|
||||
final res = await Http.post(ShopApi.bindSpreadCodeId, data: {"id": value});
|
||||
if (res != null && res['code'] == 200) {
|
||||
MyDialog.toast('推广码绑定成功', icon: const Icon(Icons.check_circle), style: ToastStyle(backgroundColor: Colors.green.withAlpha(200)));
|
||||
Get.toNamed('/vloger', arguments: {'memberId': value});
|
||||
}
|
||||
if (followed.value == 0 || followed.value == 2) {
|
||||
final res = await ImService.instance.followUser(userIDList: [value]);
|
||||
if (res.success) {
|
||||
followed.value = followed.value == 0 ? 1 : 3;
|
||||
final res = await Http.post(ShopApi.bindSpreadCodeId, data: {"id": value});
|
||||
if (res != null && res['code'] == 200) {
|
||||
MyDialog.toast('推广码绑定成功', icon: const Icon(Icons.check_circle), style: ToastStyle(backgroundColor: Colors.green.withAlpha(200)));
|
||||
Get.toNamed('/vloger', arguments: {'memberId': value});
|
||||
}
|
||||
}else{
|
||||
Get.toNamed('/vloger', arguments: {'memberId': value});
|
||||
}
|
||||
} else {
|
||||
Get.toNamed('/vloger', arguments: {'memberId': value});
|
||||
}
|
||||
}
|
||||
|
||||
@ -312,6 +315,19 @@ class ChatPageState extends State<ChatPage> {
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem<String>(
|
||||
value: 'cs',
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.qr_code_scanner, color: Colors.white, size: 18),
|
||||
SizedBox(width: 8),
|
||||
Text(
|
||||
'测试',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@ -329,6 +345,10 @@ class ChatPageState extends State<ChatPage> {
|
||||
logger.w('点击了扫一扫');
|
||||
ScanUtil.openScanner(onResult: handleScanResult);
|
||||
break;
|
||||
case 'cs':
|
||||
logger.w('去互动');
|
||||
Get.toNamed('/newFoucs');
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -424,7 +444,7 @@ class ChatPageState extends State<ChatPage> {
|
||||
child: Obx(
|
||||
() {
|
||||
final chatList = controller.chatList;
|
||||
|
||||
if (chatList.isEmpty) return EmptyTip();
|
||||
return ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: BouncingScrollPhysics(),
|
||||
@ -434,7 +454,8 @@ class ChatPageState extends State<ChatPage> {
|
||||
// logger.w(chatList[index].isCustomAdmin);
|
||||
// logger.w(chatList[index].conversation.recvOpt);
|
||||
final item = chatList[index];
|
||||
final bool quiet = [ReceiveMsgOptType.kTIMRecvMsgOpt_Not_Notify_Except_At].contains(chatList[index].conversation.recvOpt ?? 0)
|
||||
final bool quiet = [ReceiveMsgOptType.kTIMRecvMsgOpt_Not_Notify_Except_At, ReceiveMsgOptType.kTIMRecvMsgOpt_Not_Notify]
|
||||
.contains(chatList[index].conversation.recvOpt ?? 0)
|
||||
? true
|
||||
: false; // 是否设置了免打扰
|
||||
final isNoFriend = chatList[index].conversation.conversationGroupList?.contains(ConversationType.noFriend.name) ?? false;
|
||||
@ -448,8 +469,22 @@ class ChatPageState extends State<ChatPage> {
|
||||
motion: const DrawerMotion(), // 可以改成 StretchMotion 或 ScrollMotion ,DrawerMotion
|
||||
children: [
|
||||
SlidableAction(
|
||||
onPressed: (_) {
|
||||
//
|
||||
onPressed: (_) async {
|
||||
logger.w(item.conversation.toLogString());
|
||||
if (isNoFriend) {
|
||||
// 单独处理陌生人聊天 这里只能处理一条,
|
||||
// 获取所有陌生人会话
|
||||
final noFriendData = await ImService.instance.getConversationListByFilter(
|
||||
filter: V2TimConversationFilter(conversationGroup: myConversationType.ConversationType.noFriend.name),
|
||||
nextSeq: 0,
|
||||
count: 1,
|
||||
);
|
||||
await ImService.instance.clearConversationUnreadCount(
|
||||
conversationID: 'c2c_${item.conversation.userID}', cleanTimestamp: item.conversation.lastMessage!.timestamp ?? 0);
|
||||
} else {
|
||||
// 清除未读
|
||||
await ImService.instance.clearConversationUnreadCount(conversationID: item.conversation.conversationID);
|
||||
}
|
||||
},
|
||||
backgroundColor: Colors.blue,
|
||||
foregroundColor: Colors.white,
|
||||
@ -458,8 +493,30 @@ class ChatPageState extends State<ChatPage> {
|
||||
),
|
||||
if (!(isAdmin || isNoFriend))
|
||||
SlidableAction(
|
||||
onPressed: (_) {
|
||||
//
|
||||
onPressed: (_) async {
|
||||
// 设置或取消免打扰
|
||||
// logger.e(controller.info.value?.recvOpt);
|
||||
// 群聊开启免打扰 = 3 单聊开启免打扰=2,关闭免打扰为0
|
||||
if (item.conversation.type == 1) {
|
||||
logger.w('当前单聊recvopt=${item.conversation.recvOpt}');
|
||||
// 单聊
|
||||
await ImService.instance.setC2CReceiveMessageOpt(
|
||||
userIDList: ['${item.conversation.userID}'],
|
||||
opt: item.conversation.recvOpt == 2
|
||||
? ReceiveMsgOptEnum.V2TIM_RECEIVE_MESSAGE // 关闭免打扰 = 0
|
||||
: ReceiveMsgOptEnum.V2TIM_RECEIVE_NOT_NOTIFY_MESSAGE); // 3=只接收at消息,2=正常接收不接受离线
|
||||
} else if (item.conversation.type == 2) {
|
||||
//群
|
||||
logger.w('当前群聊recvopt=${item.conversation.recvOpt}');
|
||||
await ImService.instance.setGroupReceiveMessageOpt(
|
||||
groupID: item.conversation.groupID ?? '',
|
||||
opt: item.conversation.recvOpt == 3
|
||||
? ReceiveMsgOptEnum.V2TIM_RECEIVE_MESSAGE
|
||||
: ReceiveMsgOptEnum.V2TIM_RECEIVE_NOT_NOTIFY_MESSAGE_EXCEPT_AT,
|
||||
);
|
||||
}
|
||||
// 初始化一次未读总数
|
||||
Get.find<GlobalBadge>().initUnreadCount();
|
||||
},
|
||||
backgroundColor: FStyle.primaryColor,
|
||||
foregroundColor: Colors.white,
|
||||
@ -568,7 +625,7 @@ class ChatPageState extends State<ChatPage> {
|
||||
),
|
||||
onTap: () {
|
||||
if (conversationTypeFromString(chatList[index].isCustomAdmin) != null) {
|
||||
// 跳转对应的通知消息页
|
||||
// 跳转对应的通知消息页 notify下的内容
|
||||
logger.e(chatList[index].isCustomAdmin);
|
||||
Get.toNamed('/${chatList[index].isCustomAdmin}', arguments: chatList[index].conversation);
|
||||
} else if (chatList[index].conversation.conversationGroupList!.contains(ConversationType.noFriend.name)) {
|
||||
|
@ -3,10 +3,12 @@ library;
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:easy_refresh/easy_refresh.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:loopin/IM/im_service.dart';
|
||||
import 'package:loopin/behavior/custom_scroll_behavior.dart';
|
||||
import 'package:loopin/components/empty_tip.dart';
|
||||
import 'package:loopin/components/network_or_asset_image.dart';
|
||||
import 'package:loopin/models/conversation_type.dart';
|
||||
import 'package:loopin/models/notify_message.type.dart';
|
||||
@ -29,9 +31,9 @@ class Interaction extends StatefulWidget {
|
||||
|
||||
class InteractionState extends State<Interaction> with SingleTickerProviderStateMixin {
|
||||
bool isLoading = false; // 是否在加载中
|
||||
bool hasMore = true; // 是否还有更多数据
|
||||
final RxBool _throttleFlag = false.obs; // 滚动节流锁
|
||||
final ScrollController chatController = ScrollController();
|
||||
RxBool hasMore = true.obs; // 是否还有更多数据
|
||||
// final RxBool _throttleFlag = false.obs; // 滚动节流锁
|
||||
// final ScrollController chatController = ScrollController();
|
||||
String page = '';
|
||||
|
||||
///-------------------
|
||||
@ -58,24 +60,18 @@ class InteractionState extends State<Interaction> with SingleTickerProviderState
|
||||
msgList.add(lastmsg);
|
||||
}
|
||||
}
|
||||
chatController.addListener(() {
|
||||
if (_throttleFlag.value) return;
|
||||
if (chatController.position.pixels >= chatController.position.maxScrollExtent - 50) {
|
||||
_throttleFlag.value = true;
|
||||
getMsgData().then((_) {
|
||||
// 解锁
|
||||
Future.delayed(Duration(milliseconds: 1000), () {
|
||||
_throttleFlag.value = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
chatController.dispose();
|
||||
// chatController.addListener(() {
|
||||
// if (_throttleFlag.value) return;
|
||||
// if (chatController.position.pixels >= chatController.position.maxScrollExtent - 50) {
|
||||
// _throttleFlag.value = true;
|
||||
// getMsgData().then((_) {
|
||||
// // 解锁
|
||||
// Future.delayed(Duration(milliseconds: 1000), () {
|
||||
// _throttleFlag.value = false;
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
// 分页获取全部数据
|
||||
@ -90,7 +86,7 @@ class InteractionState extends State<Interaction> with SingleTickerProviderState
|
||||
if (res.success && res.data != null) {
|
||||
msgList.addAll(res.data!);
|
||||
if (res.data!.isEmpty) {
|
||||
hasMore = false;
|
||||
hasMore.value = false;
|
||||
}
|
||||
logger.i('聊天数据加载成功');
|
||||
} else {
|
||||
@ -230,135 +226,169 @@ class InteractionState extends State<Interaction> with SingleTickerProviderState
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: RefreshIndicator(
|
||||
backgroundColor: Colors.white,
|
||||
color: Color(0xFFFF5000),
|
||||
displacement: 10.0,
|
||||
onRefresh: handleRefresh,
|
||||
child: Obx(() {
|
||||
return ListView.builder(
|
||||
controller: chatController,
|
||||
shrinkWrap: true,
|
||||
physics: BouncingScrollPhysics(),
|
||||
// itemCount: msgList.length,
|
||||
itemCount: demoList.length,
|
||||
itemBuilder: (context, index) {
|
||||
//检测cloudCustomData
|
||||
// interactionComment, //互动->评论
|
||||
// interactionAt, //互动->视频评论中的@
|
||||
// interactionLike, //互动->点赞
|
||||
// interactionReply, //互动->评论回复
|
||||
child: EasyRefresh.builder(
|
||||
callLoadOverOffset: 20, //触底距离
|
||||
callRefreshOverOffset: 20, // 下拉距离
|
||||
header: ClassicHeader(
|
||||
dragText: '下拉刷新',
|
||||
armedText: '释放刷新',
|
||||
readyText: '加载中...',
|
||||
processingText: '加载中...',
|
||||
processedText: '加载完成',
|
||||
failedText: '加载失败,请重试',
|
||||
messageText: '最后更新于 %T',
|
||||
),
|
||||
footer: ClassicFooter(
|
||||
dragText: '加载更多',
|
||||
armedText: '释放加载',
|
||||
readyText: '加载中...',
|
||||
processingText: '加载中...',
|
||||
processedText: '加载完成',
|
||||
noMoreText: '没有更多了~',
|
||||
failedText: '加载失败,请重试',
|
||||
messageText: '最后更新于 %T',
|
||||
),
|
||||
onRefresh: () async {
|
||||
await handleRefresh();
|
||||
},
|
||||
onLoad: () async {
|
||||
if (hasMore.value) {
|
||||
await getMsgData();
|
||||
return hasMore.value ? IndicatorResult.success : IndicatorResult.noMore;
|
||||
}
|
||||
},
|
||||
childBuilder: (context, physics) {
|
||||
return Obx(
|
||||
() {
|
||||
// if (msgList.isEmpty) return EmptyTip();
|
||||
if (demoList.isEmpty) return EmptyTip();
|
||||
return ListView.builder(
|
||||
// controller: chatController,
|
||||
shrinkWrap: true,
|
||||
physics: physics,
|
||||
// itemCount: msgList.length,
|
||||
itemCount: demoList.length,
|
||||
itemBuilder: (context, index) {
|
||||
//检测cloudCustomData
|
||||
// interactionComment, //互动->评论 评论
|
||||
// interactionAt, //互动->视频评论中的@ 评论
|
||||
// interactionLike, //互动->点赞 视频(暂时不涉及评论点赞)
|
||||
// interactionReply, //互动->评论回复 评论
|
||||
|
||||
//----
|
||||
// final element =msgList[index].customElem!;
|
||||
// final cloudCustomData = msgList[index].cloudCustomData;
|
||||
// final desc = msgList[index].customElem!.desc!;
|
||||
// final jsonData = msgList[index].customElem!.data;
|
||||
//----
|
||||
// final element =msgList[index].customElem!;
|
||||
// final cloudCustomData = msgList[index].cloudCustomData;
|
||||
// final desc = msgList[index].customElem!.desc!;
|
||||
// final jsonData = msgList[index].customElem!.data;
|
||||
|
||||
// ----测试数据
|
||||
final jsonData = '{"faceUrl":"","nickName":"测试昵称","userID":"213213"}';
|
||||
final item = jsonDecode(jsonData); // 数据
|
||||
final desc = '测试desc';
|
||||
final cloudCustomData = 'interactionLike';
|
||||
V2TimMessage element = V2TimMessage(elemType: 2, isRead: index > 2 ? true : false);
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
|
||||
child: Row(
|
||||
spacing: 10.0,
|
||||
children: <Widget>[
|
||||
// 头像
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
// 点击头像转到对方主页
|
||||
// 先获取视频详情
|
||||
// 如果cloudCustomData是interactionComment,interactionAt,interactionReply,传参时带上评论id,
|
||||
//
|
||||
// final res = await Http.get('${VideoApi.detail}/${item['vlogID']}');
|
||||
// Get.toNamed('/vloger', arguments: res['data']);
|
||||
Get.toNamed('/vloger');
|
||||
},
|
||||
child: ClipOval(
|
||||
child: NetworkOrAssetImage(
|
||||
imageUrl: item['faceUrl'],
|
||||
width: 50,
|
||||
height: 50,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// 消息
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
// ----测试数据
|
||||
final jsonData = '{"faceUrl":"","nickName":"测试昵称","userID":"213213"}';
|
||||
final item = jsonDecode(jsonData); // 数据
|
||||
final desc = '测试desc';
|
||||
final cloudCustomData = 'interactionLike';
|
||||
V2TimMessage element = V2TimMessage(elemType: 2, isRead: index > 2 ? true : false);
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
|
||||
child: Row(
|
||||
spacing: 10.0,
|
||||
children: <Widget>[
|
||||
// 头像
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
// 点击头像转到对方主页
|
||||
// 先获取视频详情
|
||||
// 如果cloudCustomData是interactionComment,interactionAt,interactionReply,传参时带上评论id,
|
||||
// 如果cloudCustomData是interactionComment,interactionAt,interactionReply,传参时带上评论id,这三个是评论相关的
|
||||
//
|
||||
// final res = await Http.get('${VideoApi.detail}/${item['vlogID']}');
|
||||
// 此人存在才做跳转
|
||||
// Get.toNamed('/vloger', arguments: res['data']);
|
||||
Get.toNamed('/vloger');
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
// 昵称
|
||||
Text(
|
||||
item['nickName'] ?? '未知',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2.0),
|
||||
// 描述内容
|
||||
Text(
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(color: Colors.grey, fontSize: 12.0),
|
||||
// desc,
|
||||
'很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容',
|
||||
),
|
||||
Text(
|
||||
Utils.formatTime(element.timestamp ?? DateTime.now().millisecondsSinceEpoch ~/ 1000),
|
||||
style: TextStyle(
|
||||
color: Colors.grey[600],
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// 右侧
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Visibility(
|
||||
visible: true,
|
||||
// 视频首图
|
||||
child: ClipOval(
|
||||
child: NetworkOrAssetImage(
|
||||
imageUrl: item['firstFrameImg'],
|
||||
placeholderAsset: 'assets/images/bk.jpg',
|
||||
width: 40,
|
||||
height: 60,
|
||||
imageUrl: item['faceUrl'],
|
||||
width: 50,
|
||||
height: 50,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 5.0),
|
||||
// 角标
|
||||
Visibility(
|
||||
visible: !(element.isRead ?? true),
|
||||
child: FStyle.badge(0, isdot: true),
|
||||
),
|
||||
|
||||
// 消息
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
// 点击头像转到对方主页
|
||||
// 先获取视频详情
|
||||
// 如果cloudCustomData是interactionComment,interactionAt,interactionReply,传参时带上评论id,
|
||||
//
|
||||
// final res = await Http.get('${VideoApi.detail}/${item['vlogID']}');
|
||||
// Get.toNamed('/vloger', arguments: res['data']);
|
||||
Get.toNamed('/vloger');
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
// 昵称
|
||||
Text(
|
||||
item['nickName'] ?? '未知',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2.0),
|
||||
// 描述内容
|
||||
Text(
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(color: Colors.grey, fontSize: 12.0),
|
||||
// desc,
|
||||
'很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容',
|
||||
),
|
||||
Text(
|
||||
Utils.formatTime(element.timestamp ?? DateTime.now().millisecondsSinceEpoch ~/ 1000),
|
||||
style: TextStyle(
|
||||
color: Colors.grey[600],
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
})),
|
||||
),
|
||||
|
||||
// 右侧
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Visibility(
|
||||
visible: true,
|
||||
// 视频首图
|
||||
child: NetworkOrAssetImage(
|
||||
imageUrl: item['firstFrameImg'],
|
||||
placeholderAsset: 'assets/images/bk.jpg',
|
||||
width: 40,
|
||||
height: 60,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 5.0),
|
||||
// 角标
|
||||
Visibility(
|
||||
visible: !(element.isRead ?? true),
|
||||
child: FStyle.badge(0, isdot: true),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -3,11 +3,13 @@ library;
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:easy_refresh/easy_refresh.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:loopin/IM/im_service.dart';
|
||||
import 'package:loopin/api/video_api.dart';
|
||||
import 'package:loopin/behavior/custom_scroll_behavior.dart';
|
||||
import 'package:loopin/components/empty_tip.dart';
|
||||
import 'package:loopin/components/network_or_asset_image.dart';
|
||||
import 'package:loopin/models/conversation_type.dart';
|
||||
import 'package:loopin/service/http.dart';
|
||||
@ -17,10 +19,7 @@ import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_custom_elem.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart';
|
||||
|
||||
// interactionComment, //互动->评论
|
||||
// interactionAt, //互动->视频评论中的@
|
||||
// interactionLike, //互动->点赞
|
||||
// interactionReply, //互动->评论回复
|
||||
// newFocus, 新的关注
|
||||
|
||||
class Newfoucs extends StatefulWidget {
|
||||
const Newfoucs({super.key});
|
||||
@ -31,9 +30,7 @@ class Newfoucs extends StatefulWidget {
|
||||
|
||||
class NewfoucsState extends State<Newfoucs> with SingleTickerProviderStateMixin {
|
||||
bool isLoading = false; // 是否在加载中
|
||||
bool hasMore = true; // 是否还有更多数据
|
||||
final RxBool _throttleFlag = false.obs; // 滚动节流锁
|
||||
final ScrollController chatController = ScrollController();
|
||||
RxBool hasMore = true.obs; // 是否还有更多数据
|
||||
String page = '';
|
||||
|
||||
///-------------------
|
||||
@ -53,24 +50,6 @@ class NewfoucsState extends State<Newfoucs> with SingleTickerProviderStateMixin
|
||||
msgList.add(lastmsg);
|
||||
}
|
||||
}
|
||||
chatController.addListener(() {
|
||||
if (_throttleFlag.value) return;
|
||||
if (chatController.position.pixels >= chatController.position.maxScrollExtent - 50) {
|
||||
_throttleFlag.value = true;
|
||||
getMsgData().then((_) {
|
||||
// 解锁
|
||||
Future.delayed(Duration(milliseconds: 1000), () {
|
||||
_throttleFlag.value = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
chatController.dispose();
|
||||
}
|
||||
|
||||
// 分页获取全部数据
|
||||
@ -86,7 +65,7 @@ class NewfoucsState extends State<Newfoucs> with SingleTickerProviderStateMixin
|
||||
msgList.addAll(res.data!);
|
||||
logger.e(msgList);
|
||||
if (res.data!.isEmpty) {
|
||||
hasMore = false;
|
||||
hasMore.value = false;
|
||||
}
|
||||
logger.i('聊天数据加载成功');
|
||||
} else {
|
||||
@ -138,69 +117,80 @@ class NewfoucsState extends State<Newfoucs> with SingleTickerProviderStateMixin
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: RefreshIndicator(
|
||||
backgroundColor: Colors.white,
|
||||
color: Color(0xFFFF5000),
|
||||
displacement: 10.0,
|
||||
onRefresh: handleRefresh,
|
||||
child: Obx(() {
|
||||
return ListView.builder(
|
||||
controller: chatController,
|
||||
shrinkWrap: true,
|
||||
physics: BouncingScrollPhysics(),
|
||||
itemCount: msgList.length,
|
||||
itemBuilder: (context, index) {
|
||||
//检测cloudCustomData
|
||||
child: EasyRefresh.builder(
|
||||
callLoadOverOffset: 20, //触底距离
|
||||
callRefreshOverOffset: 20, // 下拉距离
|
||||
header: ClassicHeader(
|
||||
dragText: '下拉刷新',
|
||||
armedText: '释放刷新',
|
||||
readyText: '加载中...',
|
||||
processingText: '加载中...',
|
||||
processedText: '加载完成',
|
||||
failedText: '加载失败,请重试',
|
||||
messageText: '最后更新于 %T',
|
||||
),
|
||||
footer: ClassicFooter(
|
||||
dragText: '加载更多',
|
||||
armedText: '释放加载',
|
||||
readyText: '加载中...',
|
||||
processingText: '加载中...',
|
||||
processedText: '加载完成',
|
||||
noMoreText: '没有更多了~',
|
||||
failedText: '加载失败,请重试',
|
||||
messageText: '最后更新于 %T',
|
||||
),
|
||||
onRefresh: () async {
|
||||
await handleRefresh();
|
||||
},
|
||||
onLoad: () async {
|
||||
if (hasMore.value) {
|
||||
await getMsgData();
|
||||
return hasMore.value ? IndicatorResult.success : IndicatorResult.noMore;
|
||||
}
|
||||
},
|
||||
childBuilder: (context, physics) {
|
||||
return Obx(
|
||||
() {
|
||||
if (msgList.isEmpty) return EmptyTip();
|
||||
|
||||
//----正式数据
|
||||
V2TimMessage msg = msgList[index];
|
||||
V2TimCustomElem element = msgList[index].customElem!;
|
||||
final cloudCustomData = msgList[index].cloudCustomData;
|
||||
logger.w(cloudCustomData);
|
||||
final desc = msgList[index].customElem!.desc!;
|
||||
String? jsonData = msgList[index].customElem!.data;
|
||||
jsonData = (jsonData == null || jsonData.isEmpty) ? '{"faceUrl":"","nickName":"data为空","userID":"213213"}' : jsonData;
|
||||
return ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: physics,
|
||||
itemCount: msgList.length,
|
||||
itemBuilder: (context, index) {
|
||||
//检测cloudCustomData
|
||||
///发起关注人的[userID],
|
||||
///发起关注人的昵称[nickName],
|
||||
///发起关注人的头像地址[faceUrl],
|
||||
|
||||
final item = jsonDecode(jsonData ?? '{"faceUrl":"","nickName":"测试昵称","userID":"213213"}');
|
||||
//----正式数据
|
||||
V2TimMessage msg = msgList[index];
|
||||
V2TimCustomElem element = msgList[index].customElem!;
|
||||
final cloudCustomData = msgList[index].cloudCustomData;
|
||||
logger.w(cloudCustomData);
|
||||
final desc = msgList[index].customElem!.desc!;
|
||||
String? jsonData = msgList[index].customElem!.data;
|
||||
jsonData = (jsonData == null || jsonData.isEmpty) ? '{"faceUrl":"","nickName":"data为空","userID":"213213"}' : jsonData;
|
||||
|
||||
logger.w(element.toJson());
|
||||
final item = jsonDecode(jsonData ?? '{"faceUrl":"","nickName":"测试昵称","userID":"213213"}');
|
||||
|
||||
// ----测试数据
|
||||
// final jsonData = '{"faceUrl":"","nickName":"测试昵称","userID":"213213"}';
|
||||
// final item = jsonDecode(jsonData); // 数据
|
||||
// final desc = '测试desc';
|
||||
// final cloudCustomData = 'interactionLike';
|
||||
// V2TimMessage element = V2TimMessage(elemType: 2, isRead: index > 2 ? true : false);
|
||||
// -----------
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
|
||||
child: Row(
|
||||
spacing: 10.0,
|
||||
children: <Widget>[
|
||||
// 头像
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
// 点击头像转到对方主页
|
||||
// 先获取视频详情
|
||||
// 如果cloudCustomData是interactionComment,interactionAt,interactionReply,传参时带上评论id,
|
||||
//
|
||||
final res = await Http.get('${VideoApi.detail}/${item['vlogID']}');
|
||||
Get.toNamed('/vloger', arguments: res['data']);
|
||||
// Get.toNamed('/vloger');
|
||||
},
|
||||
child: ClipOval(
|
||||
child: NetworkOrAssetImage(
|
||||
imageUrl: item['faceUrl'],
|
||||
width: 50,
|
||||
height: 50,
|
||||
),
|
||||
),
|
||||
),
|
||||
logger.w(element.toJson());
|
||||
|
||||
// 消息
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
// ----测试数据
|
||||
// final jsonData = '{"faceUrl":"","nickName":"测试昵称","userID":"213213"}';
|
||||
// final item = jsonDecode(jsonData); // 数据
|
||||
// final desc = '测试desc';
|
||||
// final cloudCustomData = 'newFocus';
|
||||
// V2TimCustomElem msg = V2TimMessage(elemType: 2, isRead: index > 2 ? true : false);
|
||||
// -----------
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
|
||||
child: Row(
|
||||
spacing: 10.0,
|
||||
children: <Widget>[
|
||||
// 头像
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
// 点击头像转到对方主页
|
||||
// 先获取视频详情
|
||||
@ -210,66 +200,87 @@ class NewfoucsState extends State<Newfoucs> with SingleTickerProviderStateMixin
|
||||
Get.toNamed('/vloger', arguments: res['data']);
|
||||
// Get.toNamed('/vloger');
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
// 昵称
|
||||
Text(
|
||||
item['nickName'] ?? '未知',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2.0),
|
||||
// 描述内容
|
||||
Text(
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(color: Colors.grey, fontSize: 12.0),
|
||||
desc,
|
||||
// '很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容',
|
||||
),
|
||||
Text(
|
||||
Utils.formatTime(msg.timestamp ?? DateTime.now().millisecondsSinceEpoch ~/ 1000),
|
||||
style: TextStyle(
|
||||
color: Colors.grey[600],
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// 右侧
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Visibility(
|
||||
visible: true,
|
||||
// 视频首图
|
||||
child: ClipOval(
|
||||
child: NetworkOrAssetImage(
|
||||
imageUrl: item['firstFrameImg'],
|
||||
placeholderAsset: 'assets/images/bk.jpg',
|
||||
width: 40,
|
||||
height: 60,
|
||||
imageUrl: item['faceUrl'],
|
||||
width: 50,
|
||||
height: 50,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 5.0),
|
||||
// 角标
|
||||
Visibility(
|
||||
visible: !(msg.isRead ?? true),
|
||||
child: FStyle.badge(0, isdot: true),
|
||||
),
|
||||
|
||||
// 消息
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
// 点击头像转到对方主页,先获取视频详情
|
||||
final res = await Http.get('${VideoApi.detail}/${item['vlogID']}');
|
||||
Get.toNamed('/vloger', arguments: res['data']);
|
||||
// Get.toNamed('/vloger');
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
// 昵称
|
||||
Text(
|
||||
item['nickName'] ?? '未知',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2.0),
|
||||
// 描述内容
|
||||
Text(
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(color: Colors.grey, fontSize: 12.0),
|
||||
desc,
|
||||
// '很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容',
|
||||
),
|
||||
Text(
|
||||
Utils.formatTime(msg.timestamp ?? DateTime.now().millisecondsSinceEpoch ~/ 1000),
|
||||
style: TextStyle(
|
||||
color: Colors.grey[600],
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
})),
|
||||
),
|
||||
|
||||
// 右侧
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Visibility(
|
||||
visible: false, // 新的关注不需要
|
||||
// 视频首图
|
||||
child: NetworkOrAssetImage(
|
||||
imageUrl: item['firstFrameImg'],
|
||||
placeholderAsset: 'assets/images/bk.jpg',
|
||||
width: 40,
|
||||
height: 60,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 5.0),
|
||||
// 角标
|
||||
Visibility(
|
||||
visible: !(msg.isRead ?? true),
|
||||
child: FStyle.badge(0, isdot: true),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -1,111 +1,81 @@
|
||||
/// 订单通知列表
|
||||
/// 新关注通知
|
||||
library;
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:easy_refresh/easy_refresh.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:loopin/IM/controller/chat_controller.dart';
|
||||
import 'package:loopin/IM/im_service.dart';
|
||||
import 'package:loopin/api/video_api.dart';
|
||||
import 'package:loopin/behavior/custom_scroll_behavior.dart';
|
||||
import 'package:loopin/components/empty_tip.dart';
|
||||
import 'package:loopin/components/network_or_asset_image.dart';
|
||||
import 'package:loopin/models/conversation_type.dart';
|
||||
import 'package:loopin/service/http.dart';
|
||||
import 'package:loopin/styles/index.dart';
|
||||
import 'package:loopin/utils/index.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_follow_type_check_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_custom_elem.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart';
|
||||
|
||||
class UserWithFollow {
|
||||
final V2TimUserFullInfo userInfo;
|
||||
int followType;
|
||||
// newFocus, 新的关注
|
||||
|
||||
UserWithFollow({
|
||||
required this.userInfo,
|
||||
this.followType = 0,
|
||||
});
|
||||
}
|
||||
|
||||
class Order extends StatefulWidget {
|
||||
const Order({super.key});
|
||||
class Newfoucs extends StatefulWidget {
|
||||
const Newfoucs({super.key});
|
||||
|
||||
@override
|
||||
State<Order> createState() => OrderState();
|
||||
State<Newfoucs> createState() => NewfoucsState();
|
||||
}
|
||||
|
||||
class OrderState extends State<Order> with SingleTickerProviderStateMixin {
|
||||
class NewfoucsState extends State<Newfoucs> with SingleTickerProviderStateMixin {
|
||||
bool isLoading = false; // 是否在加载中
|
||||
bool hasMore = true; // 是否还有更多数据
|
||||
RxBool hasMore = true.obs; // 是否还有更多数据
|
||||
String page = '';
|
||||
List<UserWithFollow> dataList = <UserWithFollow>[];
|
||||
|
||||
///-------------------
|
||||
V2TimConversation? conv;
|
||||
RxList<V2TimMessage> msgList = <V2TimMessage>[].obs;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
getData();
|
||||
if (Get.arguments != null && Get.arguments is V2TimConversation) {
|
||||
// 如果有参数
|
||||
conv = Get.arguments as V2TimConversation;
|
||||
logger.e('lastmsg:$conv');
|
||||
|
||||
final lastmsg = conv?.lastMessage;
|
||||
if (lastmsg != null) {
|
||||
msgList.add(lastmsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 分页获取陌关注列表数据
|
||||
Future<void> getData() async {
|
||||
/// 0:不是好友也没有关注
|
||||
/// 1:你关注了对方(单向)
|
||||
/// 2:对方关注了你(单向)
|
||||
/// 3:互相关注(双向好友)
|
||||
final res = await ImService.instance.getMyFollowingList(
|
||||
nextCursor: page,
|
||||
// 分页获取全部数据
|
||||
Future<void> getMsgData() async {
|
||||
// 获取最旧一条消息作为游标
|
||||
V2TimMessage? lastRealMsg;
|
||||
lastRealMsg = msgList.last;
|
||||
final res = await ImService.instance.getHistoryMessageList(
|
||||
userID: ConversationType.newFocus.name, // userID为固定的newFocus
|
||||
lastMsg: lastRealMsg,
|
||||
);
|
||||
if (res.success && res.data != null) {
|
||||
logger.i('获取成功:${res.data!.nextCursor}');
|
||||
final userInfoList = res.data!.userFullInfoList ?? [];
|
||||
// 构建数据
|
||||
List<UserWithFollow> wrappedList = userInfoList.map((u) {
|
||||
return UserWithFollow(userInfo: u);
|
||||
}).toList();
|
||||
// 获取id
|
||||
final userIDList = userInfoList.map((item) => item.userID).whereType<String>().toList();
|
||||
if (userIDList.isNotEmpty) {
|
||||
final shiRes = await ImService.instance.checkFollowType(userIDList: userIDList);
|
||||
if (shiRes.success && shiRes.data != null) {
|
||||
final shipResData = shiRes.data!;
|
||||
for (final uwf in wrappedList) {
|
||||
final userID = uwf.userInfo.userID;
|
||||
if (userID != null) {
|
||||
// 查找对应关系
|
||||
final match = shipResData.firstWhere(
|
||||
(e) => e.userID == userID,
|
||||
orElse: () => V2TimFollowTypeCheckResult(userID: ''),
|
||||
);
|
||||
if (match.userID?.isNotEmpty ?? false) {
|
||||
uwf.followType = match.followType ?? 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
msgList.addAll(res.data!);
|
||||
logger.e(msgList);
|
||||
if (res.data!.isEmpty) {
|
||||
hasMore.value = false;
|
||||
}
|
||||
final isFinished = res.data!.nextCursor == null || res.data!.nextCursor!.isEmpty;
|
||||
if (isFinished) {
|
||||
setState(() {
|
||||
hasMore = false;
|
||||
});
|
||||
// 加载没数据了
|
||||
page = '';
|
||||
} else {
|
||||
page = res.data!.nextCursor ?? '';
|
||||
}
|
||||
logger.i('获取数据成功:$userInfoList');
|
||||
setState(() {
|
||||
dataList.addAll(wrappedList);
|
||||
});
|
||||
logger.i('聊天数据加载成功');
|
||||
} else {
|
||||
logger.e('获取数据失败:${res.desc}');
|
||||
logger.e('聊天数据加载失败:${res.desc}');
|
||||
}
|
||||
}
|
||||
|
||||
// 下拉刷新
|
||||
Future<void> handleRefresh() async {
|
||||
dataList.clear();
|
||||
page = '';
|
||||
getData();
|
||||
await Future.delayed(Duration(seconds: 5));
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@ -117,15 +87,28 @@ class OrderState extends State<Order> with SingleTickerProviderStateMixin {
|
||||
centerTitle: true,
|
||||
forceMaterialTransparency: true,
|
||||
bottom: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(1.0),
|
||||
preferredSize: Size.fromHeight(1.0),
|
||||
child: Container(
|
||||
color: Colors.grey[300],
|
||||
height: 1.0,
|
||||
),
|
||||
),
|
||||
title: const Text(
|
||||
'关注',
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
title: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
'新的关注',
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [],
|
||||
),
|
||||
@ -151,7 +134,8 @@ class OrderState extends State<Order> with SingleTickerProviderStateMixin {
|
||||
armedText: '释放加载',
|
||||
readyText: '加载中...',
|
||||
processingText: '加载中...',
|
||||
processedText: hasMore ? '加载完成' : '没有更多了~',
|
||||
processedText: '加载完成',
|
||||
noMoreText: '没有更多了~',
|
||||
failedText: '加载失败,请重试',
|
||||
messageText: '最后更新于 %T',
|
||||
),
|
||||
@ -159,137 +143,139 @@ class OrderState extends State<Order> with SingleTickerProviderStateMixin {
|
||||
await handleRefresh();
|
||||
},
|
||||
onLoad: () async {
|
||||
if (hasMore) {
|
||||
await getData();
|
||||
if (hasMore.value) {
|
||||
await getMsgData();
|
||||
return hasMore.value ? IndicatorResult.success : IndicatorResult.noMore;
|
||||
}
|
||||
},
|
||||
childBuilder: (context, physics) {
|
||||
return ListView.builder(
|
||||
physics: physics,
|
||||
itemCount: dataList.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = dataList[index];
|
||||
return Ink(
|
||||
key: ValueKey(item.userInfo.userID),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
// 左侧部分(头像 + 昵称 + 描述)
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Get.toNamed(
|
||||
'/vloger',
|
||||
arguments: item.userInfo.userID,
|
||||
);
|
||||
return Obx(
|
||||
() {
|
||||
if (msgList.isEmpty) return EmptyTip();
|
||||
|
||||
return ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: physics,
|
||||
itemCount: msgList.length,
|
||||
itemBuilder: (context, index) {
|
||||
//检测cloudCustomData
|
||||
///发起关注人的[userID],
|
||||
///发起关注人的昵称[nickName],
|
||||
///发起关注人的头像地址[faceUrl],
|
||||
|
||||
//----正式数据
|
||||
V2TimMessage msg = msgList[index];
|
||||
V2TimCustomElem element = msgList[index].customElem!;
|
||||
final cloudCustomData = msgList[index].cloudCustomData;
|
||||
logger.w(cloudCustomData);
|
||||
final desc = msgList[index].customElem!.desc!;
|
||||
String? jsonData = msgList[index].customElem!.data;
|
||||
jsonData = (jsonData == null || jsonData.isEmpty) ? '{"faceUrl":"","nickName":"data为空","userID":"213213"}' : jsonData;
|
||||
|
||||
final item = jsonDecode(jsonData ?? '{"faceUrl":"","nickName":"测试昵称","userID":"213213"}');
|
||||
|
||||
logger.w(element.toJson());
|
||||
|
||||
// ----测试数据
|
||||
// final jsonData = '{"faceUrl":"","nickName":"测试昵称","userID":"213213"}';
|
||||
// final item = jsonDecode(jsonData); // 数据
|
||||
// final desc = '测试desc';
|
||||
// final cloudCustomData = 'newFocus';
|
||||
// V2TimCustomElem msg = V2TimMessage(elemType: 2, isRead: index > 2 ? true : false);
|
||||
// -----------
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
|
||||
child: Row(
|
||||
spacing: 10.0,
|
||||
children: <Widget>[
|
||||
// 头像
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
// 点击头像转到对方主页
|
||||
// 先获取视频详情
|
||||
// 如果cloudCustomData是interactionComment,interactionAt,interactionReply,传参时带上评论id,
|
||||
//
|
||||
final res = await Http.get('${VideoApi.detail}/${item['vlogID']}');
|
||||
Get.toNamed('/vloger', arguments: res['data']);
|
||||
// Get.toNamed('/vloger');
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
ClipOval(
|
||||
child: NetworkOrAssetImage(
|
||||
imageUrl: item.userInfo.faceUrl,
|
||||
width: 50,
|
||||
height: 50,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
item.userInfo.nickName?.isNotEmpty == true ? item.userInfo.nickName! : '未知昵称',
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
if (item.userInfo.selfSignature?.isNotEmpty ?? false) ...[
|
||||
const SizedBox(height: 2.0),
|
||||
Text(
|
||||
item.userInfo.selfSignature!,
|
||||
style: const TextStyle(
|
||||
color: Colors.grey,
|
||||
fontSize: 13.0,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
child: ClipOval(
|
||||
child: NetworkOrAssetImage(
|
||||
imageUrl: item['faceUrl'],
|
||||
width: 50,
|
||||
height: 50,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(width: 10),
|
||||
|
||||
// 右侧按钮
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor: item.followType == 3 ? Colors.grey : FStyle.primaryColor,
|
||||
minimumSize: const Size(70, 32),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
// 消息
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
// 点击头像转到对方主页,先获取视频详情
|
||||
final res = await Http.get('${VideoApi.detail}/${item['vlogID']}');
|
||||
Get.toNamed('/vloger', arguments: res['data']);
|
||||
// Get.toNamed('/vloger');
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
// 昵称
|
||||
Text(
|
||||
item['nickName'] ?? '未知',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2.0),
|
||||
// 描述内容
|
||||
Text(
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(color: Colors.grey, fontSize: 12.0),
|
||||
desc,
|
||||
// '很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容',
|
||||
),
|
||||
Text(
|
||||
Utils.formatTime(msg.timestamp ?? DateTime.now().millisecondsSinceEpoch ~/ 1000),
|
||||
style: TextStyle(
|
||||
color: Colors.grey[600],
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
onPressed: () async {
|
||||
final ctl = Get.find<ChatController>();
|
||||
final checkRes = await ImService.instance.checkFollowType(userIDList: [item.userInfo.userID!]);
|
||||
int realFollowType = 0;
|
||||
if (checkRes.success && checkRes.data != null) {
|
||||
realFollowType = checkRes.data!.first.followType ?? 0;
|
||||
if ([1, 3].contains(realFollowType)) {
|
||||
// 取关
|
||||
final unRes = await ImService.instance.unfollowUser(userIDList: [item.userInfo.userID!]);
|
||||
if (unRes.success) {
|
||||
setState(() {
|
||||
item.followType = 2;
|
||||
});
|
||||
ctl.mergeNoFriend(conversationID: 'c2c_${item.userInfo.userID!}');
|
||||
}
|
||||
} else {
|
||||
// 关注
|
||||
final res = await ImService.instance.followUser(userIDList: [item.userInfo.userID!]);
|
||||
if (res.success) {
|
||||
setState(() {
|
||||
item.followType = realFollowType == 0
|
||||
? 1
|
||||
: realFollowType == 2
|
||||
? 3
|
||||
: 0;
|
||||
});
|
||||
final chatRes = await ImService.instance.followUser(userIDList: [item.userInfo.userID!]);
|
||||
if (chatRes.success) {
|
||||
final res = await ImService.instance.getConversation(conversationID: 'c2c_${item.userInfo.userID}');
|
||||
if (res.success) {
|
||||
V2TimConversation conversation = res.data;
|
||||
if (conversation.conversationGroupList?.isNotEmpty ?? false) {
|
||||
await ImService.instance.deleteConversationsFromGroup(
|
||||
groupName: conversation.conversationGroupList!.first!,
|
||||
conversationIDList: [conversation.conversationID],
|
||||
);
|
||||
ctl.updateNoFriendMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
Utils.getTipText(item.followType),
|
||||
style: const TextStyle(color: Colors.white, fontSize: 14),
|
||||
|
||||
// 右侧
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Visibility(
|
||||
visible: false, // 新的关注不需要
|
||||
// 视频首图
|
||||
child: NetworkOrAssetImage(
|
||||
imageUrl: item['firstFrameImg'],
|
||||
placeholderAsset: 'assets/images/bk.jpg',
|
||||
width: 40,
|
||||
height: 60,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 5.0),
|
||||
// 角标
|
||||
Visibility(
|
||||
visible: !(msg.isRead ?? true),
|
||||
child: FStyle.badge(0, isdot: true),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
@ -163,14 +163,15 @@ class _MemberActionSheetState extends State<InviteActionSheet> {
|
||||
armedText: '释放加载',
|
||||
readyText: '加载中...',
|
||||
processingText: '加载中...',
|
||||
processedText: hasMore ? '加载完成' : '没有更多了~',
|
||||
processedText: '加载完成',
|
||||
noMoreText: '没有更多了~',
|
||||
failedText: '加载失败,请重试',
|
||||
messageText: '最后更新于 %T',
|
||||
),
|
||||
onLoad: () async {
|
||||
//
|
||||
if (hasMore) {
|
||||
await getMemberData();
|
||||
return hasMore ? IndicatorResult.success : IndicatorResult.noMore;
|
||||
}
|
||||
},
|
||||
child: ListView.builder(
|
||||
|
@ -69,6 +69,7 @@ class _MemberActionSheetState extends State<MemberActionSheet> {
|
||||
final mem = res.data!.memberInfoList ?? [];
|
||||
setState(() {
|
||||
members.addAll(mem);
|
||||
hasMore = res.data!.nextSeq == '0' ? false : true;
|
||||
loading = false;
|
||||
});
|
||||
}
|
||||
@ -197,18 +198,19 @@ class _MemberActionSheetState extends State<MemberActionSheet> {
|
||||
armedText: '释放加载',
|
||||
readyText: '加载中...',
|
||||
processingText: '加载中...',
|
||||
processedText: hasMore ? '加载完成' : '没有更多了~',
|
||||
processedText: '加载完成',
|
||||
noMoreText: '没有更多了~',
|
||||
failedText: '加载失败,请重试',
|
||||
messageText: '最后更新于 %T',
|
||||
),
|
||||
onLoad: () async {
|
||||
//
|
||||
if (hasMore) {
|
||||
if (_query.isNotEmpty) {
|
||||
await searchMember(loadMore: true);
|
||||
} else {
|
||||
await getMemberData();
|
||||
}
|
||||
if (_query.isNotEmpty && (!isFinished)) {
|
||||
await searchMember(loadMore: true);
|
||||
return !isFinished ? IndicatorResult.success : IndicatorResult.noMore;
|
||||
} else if (hasMore) {
|
||||
await getMemberData();
|
||||
return hasMore ? IndicatorResult.success : IndicatorResult.noMore;
|
||||
}
|
||||
},
|
||||
child: ListView.builder(
|
||||
|
@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:loopin/IM/im_service.dart';
|
||||
import 'package:loopin/behavior/custom_scroll_behavior.dart';
|
||||
import 'package:loopin/components/empty_tip.dart';
|
||||
import 'package:loopin/components/network_or_asset_image.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_info.dart';
|
||||
@ -103,7 +104,8 @@ class GrouplistState extends State<Grouplist> with SingleTickerProviderStateMixi
|
||||
armedText: '释放加载',
|
||||
readyText: '加载中...',
|
||||
processingText: '加载中...',
|
||||
processedText: hasMore ? '加载完成' : '没有更多了~',
|
||||
processedText: '加载完成',
|
||||
noMoreText: '没有更多了~',
|
||||
failedText: '加载失败,请重试',
|
||||
messageText: '最后更新于 %T',
|
||||
),
|
||||
@ -113,9 +115,12 @@ class GrouplistState extends State<Grouplist> with SingleTickerProviderStateMixi
|
||||
onLoad: () async {
|
||||
if (hasMore) {
|
||||
await getData();
|
||||
return hasMore ? IndicatorResult.success : IndicatorResult.noMore;
|
||||
}
|
||||
},
|
||||
childBuilder: (context, physics) {
|
||||
if (dataList.isEmpty) return EmptyTip();
|
||||
|
||||
return ListView.builder(
|
||||
physics: physics,
|
||||
itemCount: dataList.length,
|
||||
|
@ -135,7 +135,6 @@ class _StartGroupChatPageState extends State<StartGroupChatPage> {
|
||||
msg: msgRes.data!.messageInfo!,
|
||||
groupID: groupID,
|
||||
isExcludedFromUnreadCount: true,
|
||||
isPush: false,
|
||||
groupName: groupName,
|
||||
cloudCustomData: 'tips',
|
||||
);
|
||||
@ -304,13 +303,17 @@ class _StartGroupChatPageState extends State<StartGroupChatPage> {
|
||||
armedText: '释放加载',
|
||||
readyText: '加载中...',
|
||||
processingText: '加载中...',
|
||||
processedText: hasMore ? '加载完成' : '没有更多了~',
|
||||
processedText: '加载完成',
|
||||
failedText: '加载失败,请重试',
|
||||
noMoreText: '没有更多了~',
|
||||
messageText: '最后更新于 %T',
|
||||
),
|
||||
onRefresh: () async => _loadData(reset: true),
|
||||
onLoad: () async {
|
||||
if (hasMore) await _loadData();
|
||||
if (hasMore) {
|
||||
await _loadData();
|
||||
return hasMore ? IndicatorResult.success : IndicatorResult.noMore;
|
||||
}
|
||||
},
|
||||
child: filteredList.isEmpty
|
||||
? _emptyTip('暂无数据')
|
||||
|
@ -4,6 +4,7 @@ import 'package:loopin/IM/controller/im_user_info_controller.dart';
|
||||
import 'package:loopin/components/my_qrcode.dart';
|
||||
import 'package:loopin/pages/my/merchant/balance/balance.dart';
|
||||
import 'package:loopin/pages/my/merchant/balance/controller.dart';
|
||||
import 'package:loopin/utils/index.dart';
|
||||
import 'package:loopin/utils/scan_code_type.dart';
|
||||
|
||||
class AllFunctionsPage extends StatefulWidget {
|
||||
@ -20,8 +21,8 @@ class _AllFunctionsPageState extends State<AllFunctionsPage> {
|
||||
'title': '主页展示',
|
||||
'items': [
|
||||
{'id': 'home_order', 'icon': 'assets/images/ico_order.png', 'label': '订单'},
|
||||
{'id': 'home_balance', 'icon': 'assets/images/ico_dhx.png', 'label': '余额logout'},
|
||||
{'id': 'home_withdraw', 'icon': 'assets/images/ico_sh.png', 'label': '提现vloger'},
|
||||
{'id': 'home_balance', 'icon': 'assets/images/ico_dhx.png', 'label': '余额'},
|
||||
{'id': 'home_hym', 'icon': 'assets/images/ico_tgm.png', 'label': '好友码'},
|
||||
{'id': 'home_promo', 'icon': 'assets/images/ico_tgm.png', 'label': '推广码'},
|
||||
]
|
||||
},
|
||||
@ -72,7 +73,13 @@ class _AllFunctionsPageState extends State<AllFunctionsPage> {
|
||||
itemCount: functionList.length,
|
||||
itemBuilder: (context, sectionIndex) {
|
||||
final section = functionList[sectionIndex];
|
||||
return _buildSection(section);
|
||||
final role = controller.role.value;
|
||||
final isSeller = Utils.hasRole(role, 2);
|
||||
if (section['title'] == '更多功能' && !isSeller) {
|
||||
return SizedBox();
|
||||
} else {
|
||||
return _buildSection(section);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
@ -80,6 +87,8 @@ class _AllFunctionsPageState extends State<AllFunctionsPage> {
|
||||
}
|
||||
|
||||
Widget _buildSection(Map<String, dynamic> section) {
|
||||
final role = controller.role.value;
|
||||
final isSeller = Utils.hasRole(role, 2);
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@ -112,7 +121,8 @@ class _AllFunctionsPageState extends State<AllFunctionsPage> {
|
||||
itemBuilder: (context, index) {
|
||||
final item = (section['items'] as List)[index];
|
||||
final role = controller.role.value;
|
||||
if (item['id'] == 'home_promo' && role != 4) {
|
||||
final hasRole = Utils.hasRole(role, 5);
|
||||
if (item['id'] == 'home_promo' && !hasRole) {
|
||||
return SizedBox();
|
||||
} else {
|
||||
return _buildFunctionItem(item);
|
||||
@ -121,7 +131,8 @@ class _AllFunctionsPageState extends State<AllFunctionsPage> {
|
||||
),
|
||||
|
||||
// 分隔线 - 只在不是最后一个section时显示
|
||||
if (functionList.indexOf(section) != functionList.length - 1)
|
||||
// if (functionList.indexOf(section) != functionList.length - 1)
|
||||
if (isSeller)
|
||||
Container(
|
||||
height: 2,
|
||||
margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 20),
|
||||
@ -191,14 +202,12 @@ class _AllFunctionsPageState extends State<AllFunctionsPage> {
|
||||
Get.to(() => Balance());
|
||||
// showLogoutDialog(context);
|
||||
break;
|
||||
case 'home_withdraw':
|
||||
Get.toNamed('/vloger');
|
||||
case 'home_hym':
|
||||
qrcodeAlertDialog(context);
|
||||
break;
|
||||
case 'home_promo':
|
||||
// 推广码
|
||||
Get.to(() => MyQrcode(
|
||||
prefix: QrTypeCode.tgm,
|
||||
));
|
||||
qrcodeAlertDialog(context);
|
||||
break;
|
||||
case 'more_seller_order':
|
||||
Get.toNamed('/sellerOrder');
|
||||
@ -209,6 +218,41 @@ class _AllFunctionsPageState extends State<AllFunctionsPage> {
|
||||
}
|
||||
}
|
||||
|
||||
// 二维码名片弹窗
|
||||
void qrcodeAlertDialog(BuildContext context) {
|
||||
final role = controller.role.value;
|
||||
final isLeader = Utils.hasRole(role, 5);
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return UnconstrainedBox(
|
||||
constrainedAxis: Axis.vertical,
|
||||
child: SizedBox(
|
||||
width: 350.0,
|
||||
child: AlertDialog(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
|
||||
backgroundColor: Colors.white,
|
||||
surfaceTintColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)),
|
||||
content: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
MyQrcode(
|
||||
prefix: QrTypeCode.tgm,
|
||||
text: isLeader ? '推广码' : '',
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// 退出登录弹窗
|
||||
void showLogoutDialog(BuildContext context) {
|
||||
showDialog(
|
||||
|
268
lib/pages/my/delete.dart
Normal file
268
lib/pages/my/delete.dart
Normal file
@ -0,0 +1,268 @@
|
||||
/// 注册模板
|
||||
library;
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:loopin/IM/im_service.dart';
|
||||
import 'package:loopin/api/common_api.dart';
|
||||
import 'package:loopin/controller/video_module_controller.dart';
|
||||
import 'package:loopin/service/http.dart';
|
||||
import 'package:loopin/utils/common.dart';
|
||||
import 'package:shirne_dialog/shirne_dialog.dart';
|
||||
|
||||
import '../../utils/index.dart';
|
||||
|
||||
class Delete extends StatefulWidget {
|
||||
const Delete({super.key});
|
||||
|
||||
@override
|
||||
State<Delete> createState() => _DeleteState();
|
||||
}
|
||||
|
||||
class _DeleteState extends State<Delete> {
|
||||
final Map authObj = {'phonenumber': '', 'smsCode': '', 'clientId': '428a8310cd442757ae699df5d894f051', 'grantType': 'sms'};
|
||||
|
||||
final fieldController = TextEditingController();
|
||||
Timer? timer;
|
||||
String vcodeText = '获取验证码';
|
||||
bool disabled = false;
|
||||
int time = 60;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
timer?.cancel();
|
||||
}
|
||||
|
||||
// 清空文本框
|
||||
void handleClear() {
|
||||
fieldController.clear();
|
||||
setState(() {
|
||||
authObj['phonenumber'] = '';
|
||||
});
|
||||
}
|
||||
|
||||
//先退出,在注销
|
||||
void handleLogout() async {}
|
||||
|
||||
// 提交表单
|
||||
void handleSubmit() async {
|
||||
FocusScope.of(context).unfocus(); // 收起键盘
|
||||
if (authObj['phonenumber'] == '') {
|
||||
MyDialog.toast('手机号不能为空', icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
||||
} else if (!Utils.checkTel(authObj['phonenumber'])) {
|
||||
MyDialog.toast('手机号格式不正确', icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
||||
} else if (authObj['smsCode'] == '') {
|
||||
MyDialog.toast('验证码不能为空', icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
||||
} else {
|
||||
final dialogController = MyDialog.loading('注销中...');
|
||||
// 执行退出登录逻辑,执行注销逻辑
|
||||
final loginRes = await ImService.instance.logout();
|
||||
if (loginRes.success) {
|
||||
// 清除存储信息
|
||||
Common.logout();
|
||||
// 初始化视频
|
||||
final videoController = Get.find<VideoModuleController>();
|
||||
videoController.init();
|
||||
// 执行主席到逻辑
|
||||
final del = await Http.post('${CommonApi.revoked}?smsCode=${authObj['smsCode']}');
|
||||
logger.w(del);
|
||||
//
|
||||
Get.offAllNamed('/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 60s倒计时
|
||||
void handleVcode() {
|
||||
logger.i(authObj);
|
||||
FocusScope.of(context).unfocus(); // 收起键盘
|
||||
if (authObj['phonenumber'] == '') {
|
||||
MyDialog.toast('手机号不能为空', icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
||||
} else if (!Utils.checkTel(authObj['phonenumber'])) {
|
||||
MyDialog.toast('手机号格式不正确', icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
||||
} else {
|
||||
setState(() {
|
||||
disabled = true;
|
||||
});
|
||||
startTimer();
|
||||
getCode();
|
||||
}
|
||||
}
|
||||
|
||||
startTimer() {
|
||||
timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
setState(() {
|
||||
if (time > 0) {
|
||||
vcodeText = '获取验证码(${time--})';
|
||||
} else {
|
||||
vcodeText = '获取验证码';
|
||||
time = 60;
|
||||
disabled = false;
|
||||
timer.cancel();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void getCode() async {
|
||||
//
|
||||
final res = await Http.get('${CommonApi.dictionaryApi}sms_template_id');
|
||||
final dictData = res['data'] as List;
|
||||
final templeId = dictData.first['dictValue'];
|
||||
logger.w(templeId);
|
||||
final resCode = await Http.get(CommonApi.getDelCode, params: {'templateId': templeId});
|
||||
logger.i('注销验证短信:$resCode');
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final phoneNum = authObj['phonenumber'] as String;
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onTap: () => FocusScope.of(context).unfocus(),
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
'注销账号',
|
||||
style: TextStyle(color: Colors.black),
|
||||
),
|
||||
forceMaterialTransparency: true,
|
||||
),
|
||||
body: Container(
|
||||
alignment: Alignment.center,
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
height: 40.0,
|
||||
margin: const EdgeInsets.symmetric(vertical: 5.0, horizontal: 30.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFFAF8F5),
|
||||
borderRadius: BorderRadius.circular(15.0),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
keyboardType: TextInputType.phone,
|
||||
controller: fieldController,
|
||||
inputFormatters: [
|
||||
LengthLimitingTextInputFormatter(11), // 限制输入长度为 11
|
||||
FilteringTextInputFormatter.digitsOnly, // 限制只能输入数字
|
||||
],
|
||||
decoration: InputDecoration(
|
||||
hintText: '输入手机号',
|
||||
hintStyle: const TextStyle(color: Colors.black38),
|
||||
suffixIcon: Visibility(
|
||||
visible: phoneNum.isNotEmpty,
|
||||
child: InkWell(
|
||||
hoverColor: Colors.transparent,
|
||||
highlightColor: Colors.transparent,
|
||||
splashColor: Colors.transparent,
|
||||
onTap: handleClear,
|
||||
child: const Icon(
|
||||
Icons.clear,
|
||||
color: Colors.grey,
|
||||
size: 16.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 0, horizontal: 12.0),
|
||||
border: const OutlineInputBorder(borderSide: BorderSide.none),
|
||||
),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
authObj['phonenumber'] = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 40.0,
|
||||
margin: const EdgeInsets.symmetric(vertical: 5.0, horizontal: 30.0),
|
||||
decoration: BoxDecoration(color: Color(0xFFFAF8F5), borderRadius: BorderRadius.circular(15.0)),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
keyboardType: TextInputType.phone,
|
||||
inputFormatters: [
|
||||
LengthLimitingTextInputFormatter(6),
|
||||
FilteringTextInputFormatter.digitsOnly, // 限制只能输入数字
|
||||
],
|
||||
decoration: const InputDecoration(
|
||||
hintText: '验证码',
|
||||
hintStyle: TextStyle(color: Colors.black38),
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 0, horizontal: 12.0),
|
||||
border: OutlineInputBorder(borderSide: BorderSide.none),
|
||||
),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
authObj['smsCode'] = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 25.0,
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(right: 8.0),
|
||||
child: ElevatedButton(
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStateProperty.all(Colors.white),
|
||||
shape: WidgetStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0))),
|
||||
padding: WidgetStateProperty.all(EdgeInsets.symmetric(horizontal: 15.0))),
|
||||
onPressed: !disabled ? handleVcode : null,
|
||||
child: Text(vcodeText, style: const TextStyle(fontSize: 13.0)),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'提示:\n'
|
||||
'1. 账号注销后,平台会保留您的数据 7 天。\n'
|
||||
'2. 若 7 天内未登录,平台将销毁所有保留数据。\n'
|
||||
'3. 若在 7 天内重新登录,则视为放弃注销。\n'
|
||||
'请谨慎操作。',
|
||||
style: TextStyle(fontSize: 14, color: Colors.red[700]),
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 30.0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15.0),
|
||||
),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
height: 45.0,
|
||||
child: FilledButton(
|
||||
style: ButtonStyle(shape: WidgetStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.circular(30.0)))),
|
||||
onPressed: handleSubmit,
|
||||
child: const Text(
|
||||
'确认注销',
|
||||
style: TextStyle(fontSize: 16.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10.0,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ import 'package:get/get.dart';
|
||||
import 'package:loopin/IM/controller/chat_controller.dart';
|
||||
import 'package:loopin/IM/im_service.dart';
|
||||
import 'package:loopin/behavior/custom_scroll_behavior.dart';
|
||||
import 'package:loopin/components/empty_tip.dart';
|
||||
import 'package:loopin/components/network_or_asset_image.dart';
|
||||
import 'package:loopin/styles/index.dart';
|
||||
import 'package:loopin/utils/index.dart';
|
||||
@ -151,7 +152,8 @@ class FansState extends State<Fans> with SingleTickerProviderStateMixin {
|
||||
armedText: '释放加载',
|
||||
readyText: '加载中...',
|
||||
processingText: '加载中...',
|
||||
processedText: hasMore ? '加载完成' : '没有更多了~',
|
||||
processedText: '加载完成',
|
||||
noMoreText: '没有更多了~',
|
||||
failedText: '加载失败,请重试',
|
||||
messageText: '最后更新于 %T',
|
||||
),
|
||||
@ -161,9 +163,12 @@ class FansState extends State<Fans> with SingleTickerProviderStateMixin {
|
||||
onLoad: () async {
|
||||
if (hasMore) {
|
||||
await getData();
|
||||
return hasMore ? IndicatorResult.success : IndicatorResult.noMore;
|
||||
}
|
||||
},
|
||||
childBuilder: (context, physics) {
|
||||
if (dataList.isEmpty) return EmptyTip();
|
||||
|
||||
return ListView.builder(
|
||||
physics: physics,
|
||||
itemCount: dataList.length,
|
||||
|
@ -7,6 +7,7 @@ import 'package:get/get.dart';
|
||||
import 'package:loopin/IM/controller/chat_controller.dart';
|
||||
import 'package:loopin/IM/im_service.dart';
|
||||
import 'package:loopin/behavior/custom_scroll_behavior.dart';
|
||||
import 'package:loopin/components/empty_tip.dart';
|
||||
import 'package:loopin/components/network_or_asset_image.dart';
|
||||
import 'package:loopin/styles/index.dart';
|
||||
import 'package:loopin/utils/index.dart';
|
||||
@ -151,7 +152,8 @@ class FlowingState extends State<Flowing> with SingleTickerProviderStateMixin {
|
||||
armedText: '释放加载',
|
||||
readyText: '加载中...',
|
||||
processingText: '加载中...',
|
||||
processedText: hasMore ? '加载完成' : '没有更多了~',
|
||||
processedText: '加载完成',
|
||||
noMoreText: '没有更多了~',
|
||||
failedText: '加载失败,请重试',
|
||||
messageText: '最后更新于 %T',
|
||||
),
|
||||
@ -161,9 +163,12 @@ class FlowingState extends State<Flowing> with SingleTickerProviderStateMixin {
|
||||
onLoad: () async {
|
||||
if (hasMore) {
|
||||
await getData();
|
||||
return hasMore ? IndicatorResult.success : IndicatorResult.noMore;
|
||||
}
|
||||
},
|
||||
childBuilder: (context, physics) {
|
||||
if (dataList.isEmpty) return EmptyTip();
|
||||
|
||||
return ListView.builder(
|
||||
physics: physics,
|
||||
itemCount: dataList.length,
|
||||
|
@ -13,8 +13,9 @@ import 'package:loopin/components/my_qrcode.dart';
|
||||
import 'package:loopin/components/network_or_asset_image.dart';
|
||||
import 'package:loopin/components/only_down_scroll_physics.dart';
|
||||
import 'package:loopin/controller/video_module_controller.dart';
|
||||
import 'package:loopin/pages/my/merchant/balance/balance.dart';
|
||||
import 'package:loopin/pages/my/merchant/balance/controller.dart';
|
||||
import 'package:loopin/service/http.dart';
|
||||
import 'package:loopin/styles/index.dart';
|
||||
import 'package:loopin/utils/index.dart';
|
||||
import 'package:loopin/utils/scan_code_type.dart';
|
||||
import 'package:nested_scroll_view_plus/nested_scroll_view_plus.dart';
|
||||
@ -279,6 +280,8 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
|
||||
// 二维码名片弹窗
|
||||
void qrcodeAlertDialog(BuildContext context) {
|
||||
final role = imUserInfoController?.role.value ?? 0;
|
||||
final isLeader = Utils.hasRole(role, 5);
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
@ -288,7 +291,7 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
width: 350.0,
|
||||
child: AlertDialog(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
|
||||
backgroundColor: const Color(0xff07c160),
|
||||
backgroundColor: Colors.white,
|
||||
surfaceTintColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)),
|
||||
content: Padding(
|
||||
@ -296,14 +299,10 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
MyQrcode(prefix: QrTypeCode.tgm)
|
||||
// Image.asset('assets/images/pic1.jpg', width: 250.0, fit: BoxFit.contain),
|
||||
// const SizedBox(height: 15.0),
|
||||
// const Text('扫一扫,加好友',
|
||||
// style: TextStyle(
|
||||
// color: Colors.white38,
|
||||
// fontSize: 14.0,
|
||||
// )),
|
||||
MyQrcode(
|
||||
prefix: QrTypeCode.tgm,
|
||||
text: isLeader ? '推广码' : '',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -748,6 +747,7 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
),
|
||||
child: Text(
|
||||
nickname.isNotEmpty ? nickname : '昵称',
|
||||
// '啊啊啊啊啊啊啊啊',
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
@ -759,51 +759,52 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
),
|
||||
|
||||
// 团长的二维码
|
||||
Obx(
|
||||
() {
|
||||
// MERCHANT(2, "商家"),
|
||||
// AGENT(3, "代理"),
|
||||
// PLATFORM(4, "平台"),
|
||||
// REFERENCE(5, "团长")
|
||||
final role = imUserInfoController?.role.value;
|
||||
if (role == 4) {
|
||||
return Row(children: [
|
||||
SizedBox(width: 8),
|
||||
//
|
||||
InkWell(
|
||||
onTap: () => qrcodeAlertDialog(context),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withAlpha(176),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.qr_code_outlined,
|
||||
size: 18,
|
||||
color: FStyle.secondaryColor,
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
Text(
|
||||
'团长邀请码',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: FStyle.secondaryColor, // 彩色文字
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
]);
|
||||
} else {
|
||||
return const SizedBox();
|
||||
}
|
||||
},
|
||||
),
|
||||
// Obx(
|
||||
// () {
|
||||
// // MERCHANT(2, "商家"),
|
||||
// // AGENT(3, "代理"),
|
||||
// // PLATFORM(4, "平台"),
|
||||
// // REFERENCE(5, "团长")
|
||||
// final role = imUserInfoController?.role.value;
|
||||
// final isLeader = Utils.hasRole(role ?? 0, 5);
|
||||
// if (!isLeader) {
|
||||
// return Row(children: [
|
||||
// SizedBox(width: 8),
|
||||
// //
|
||||
// InkWell(
|
||||
// onTap: () => qrcodeAlertDialog(context),
|
||||
// child: Container(
|
||||
// padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
|
||||
// decoration: BoxDecoration(
|
||||
// color: Colors.black.withAlpha(176),
|
||||
// borderRadius: BorderRadius.circular(20),
|
||||
// ),
|
||||
// child: Row(
|
||||
// children: [
|
||||
// Icon(
|
||||
// Icons.qr_code_outlined,
|
||||
// size: 18,
|
||||
// color: FStyle.secondaryColor,
|
||||
// ),
|
||||
// SizedBox(width: 4),
|
||||
// Text(
|
||||
// '团长邀请码',
|
||||
// style: TextStyle(
|
||||
// fontSize: 12,
|
||||
// fontWeight: FontWeight.bold,
|
||||
// color: FStyle.secondaryColor, // 彩色文字
|
||||
// ),
|
||||
// )
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ]);
|
||||
// } else {
|
||||
// return const SizedBox();
|
||||
// }
|
||||
// },
|
||||
// ),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@ -907,6 +908,8 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
}
|
||||
|
||||
Widget _buildOrderCard(BuildContext context) {
|
||||
final role = imUserInfoController?.role.value ?? 0;
|
||||
final isLeader = Utils.hasRole(role, 5);
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
@ -946,15 +949,16 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
_buildOrderIcon('assets/images/ico_order.png', '订单', () {
|
||||
Get.toNamed('/myOrder');
|
||||
}),
|
||||
_buildOrderIcon('assets/images/ico_dhx.png', '余额logout', () {
|
||||
_buildOrderIcon('assets/images/ico_dhx.png', '余额', () {
|
||||
Get.put(BalanceController());
|
||||
Get.to(() => Balance());
|
||||
}),
|
||||
_buildOrderIcon('assets/images/ico_tgm.png', isLeader ? '推广码' : '好友码', () {
|
||||
qrcodeAlertDialog(context);
|
||||
}),
|
||||
_buildOrderIcon('assets/images/icon_logout.png', '退出登录', () {
|
||||
showLogoutDialog(context);
|
||||
}),
|
||||
_buildOrderIcon('assets/images/ico_sh.png', '提现vloger', () {
|
||||
Get.toNamed('/vloger');
|
||||
}),
|
||||
_buildOrderIcon('assets/images/ico_tgm.png', '推广码', () {
|
||||
logger.e('推广码');
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -2,9 +2,11 @@
|
||||
import 'package:easy_refresh/easy_refresh.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:loopin/IM/im_service.dart';
|
||||
import 'package:loopin/IM/controller/im_user_info_controller.dart';
|
||||
import 'package:loopin/behavior/custom_scroll_behavior.dart';
|
||||
import 'package:loopin/components/empty_tip.dart';
|
||||
import 'package:loopin/pages/my/merchant/balance/controller.dart';
|
||||
import 'package:loopin/utils/index.dart';
|
||||
|
||||
class Balance extends StatefulWidget {
|
||||
const Balance({super.key});
|
||||
@ -14,12 +16,18 @@ class Balance extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _BalanceState extends State<Balance> {
|
||||
final controller = Get.put(BalanceController());
|
||||
final controller = Get.find<BalanceController>();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
Get.delete<BalanceController>();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text("我的钱包")),
|
||||
appBar: AppBar(title: Text("我的余额")),
|
||||
body: ScrollConfiguration(
|
||||
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
||||
child: Column(
|
||||
@ -30,6 +38,9 @@ class _BalanceState extends State<Balance> {
|
||||
color: Colors.blueAccent,
|
||||
width: double.infinity,
|
||||
child: Obx(() {
|
||||
final ctl = Get.find<ImUserInfoController>();
|
||||
final role = ctl.role.value;
|
||||
final isLeader = Utils.hasRole(role, 5);
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -46,13 +57,35 @@ class _BalanceState extends State<Balance> {
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
ElevatedButton(
|
||||
onPressed: () => controller.recharge(100.0),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.orange,
|
||||
),
|
||||
child: const Text("充值 100 元"),
|
||||
// if (isLeader) const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
// 加充值
|
||||
controller.recharge(money: '0.1');
|
||||
// http
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.orange,
|
||||
),
|
||||
child: const Text("充值"),
|
||||
),
|
||||
SizedBox(width: 20),
|
||||
//
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
// 提现
|
||||
// controller.recharge(money: '0.1');
|
||||
controller.withDraw(money: '1000');
|
||||
// http
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.orange,
|
||||
),
|
||||
child: const Text("提现"),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
@ -61,61 +94,74 @@ class _BalanceState extends State<Balance> {
|
||||
|
||||
// 下半部分:流水记录分页列表
|
||||
Expanded(
|
||||
child: Obx(() {
|
||||
return EasyRefresh.builder(
|
||||
callLoadOverOffset: 20, //触底距离
|
||||
callRefreshOverOffset: 20, // 下拉距离
|
||||
header: ClassicHeader(
|
||||
dragText: '下拉刷新',
|
||||
armedText: '释放刷新',
|
||||
readyText: '加载中...',
|
||||
processingText: '加载中...',
|
||||
processedText: '加载完成',
|
||||
failedText: '加载失败,请重试',
|
||||
messageText: '最后更新于 %T',
|
||||
),
|
||||
footer: ClassicFooter(
|
||||
dragText: '加载更多',
|
||||
armedText: '释放加载',
|
||||
readyText: '加载中...',
|
||||
processingText: '加载中...',
|
||||
processedText: controller.hasMore.value ? '加载完成' : '没有更多了~',
|
||||
failedText: '加载失败,请重试',
|
||||
messageText: '最后更新于 %T',
|
||||
succeededIcon: controller.hasMore.value ? Icon(Icons.check_circle, color: Colors.green) : Icon(Icons.warning, color: Colors.orange),
|
||||
),
|
||||
onRefresh: () async {
|
||||
await controller.getData(reset: true);
|
||||
},
|
||||
onLoad: () async {
|
||||
if (controller.hasMore.value) {
|
||||
await controller.getData();
|
||||
}
|
||||
},
|
||||
childBuilder: (context, physics) {
|
||||
return ListView.builder(
|
||||
physics: physics,
|
||||
itemCount: controller.data.length,
|
||||
itemBuilder: (context, index) {
|
||||
final tx = controller.data[index];
|
||||
logger.w(tx.source);
|
||||
child: EasyRefresh.builder(
|
||||
callLoadOverOffset: 20, //触底距离
|
||||
callRefreshOverOffset: 20, // 下拉距离
|
||||
header: ClassicHeader(
|
||||
dragText: '下拉刷新',
|
||||
armedText: '释放刷新',
|
||||
readyText: '加载中...',
|
||||
processingText: '加载中...',
|
||||
processedText: '加载完成',
|
||||
failedText: '加载失败,请重试',
|
||||
messageText: '最后更新于 %T',
|
||||
),
|
||||
footer: ClassicFooter(
|
||||
dragText: '加载更多',
|
||||
armedText: '释放加载',
|
||||
readyText: '加载中...',
|
||||
processingText: '加载中...',
|
||||
processedText: '加载完成',
|
||||
noMoreText: '没有更多了~',
|
||||
failedText: '加载失败,请重试',
|
||||
messageText: '最后更新于 %T',
|
||||
),
|
||||
|
||||
return ListTile(
|
||||
title: Text(tx.source),
|
||||
subtitle: Text(tx.createTime),
|
||||
trailing: Text(
|
||||
"变动金额:${tx.changeType == 1 ? '+' : '-'}${tx.changeAmount}", // 变动金额
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: tx.changeType == 1 ? Colors.green : Colors.red,
|
||||
),
|
||||
),
|
||||
onRefresh: () async {
|
||||
await controller.getData(reset: true);
|
||||
},
|
||||
onLoad: () async {
|
||||
if (controller.hasMore.value) {
|
||||
await controller.getData();
|
||||
return controller.hasMore.value ? IndicatorResult.success : IndicatorResult.noMore;
|
||||
}
|
||||
},
|
||||
childBuilder: (context, physics) {
|
||||
return Obx(
|
||||
() {
|
||||
// 数据加载中
|
||||
if (controller.isLoading.value && controller.data.isEmpty) {
|
||||
return Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}),
|
||||
}
|
||||
//空
|
||||
if (controller.data.isEmpty) {
|
||||
return EmptyTip();
|
||||
}
|
||||
return ListView.builder(
|
||||
physics: physics,
|
||||
itemCount: controller.data.length,
|
||||
itemBuilder: (context, index) {
|
||||
final tx = controller.data[index];
|
||||
|
||||
return ListTile(
|
||||
title: Text(tx.source),
|
||||
subtitle: Text(tx.createTime),
|
||||
trailing: Text(
|
||||
"变动金额:${tx.changeType == 1 ? '+' : '-'}${tx.changeAmount}${tx.id}", // 变动金额
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: tx.changeType == 1 ? Colors.green : Colors.red,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -1,5 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:loopin/IM/controller/im_user_info_controller.dart';
|
||||
import 'package:loopin/IM/im_friend_listeners.dart';
|
||||
import 'package:loopin/api/common_api.dart';
|
||||
import 'package:loopin/pages/my/merchant/balance/model.dart';
|
||||
import 'package:loopin/service/http.dart';
|
||||
import 'package:loopin/utils/wxsdk.dart';
|
||||
|
||||
class BalanceController extends GetxController {
|
||||
/// 钱包余额
|
||||
@ -21,36 +27,130 @@ class BalanceController extends GetxController {
|
||||
}
|
||||
|
||||
/// 充值
|
||||
void recharge(double amount) {
|
||||
balance.value += amount;
|
||||
// 同时加一条流水
|
||||
Future<void> recharge({required String money}) async {
|
||||
// 获取支付参数
|
||||
final data = {"orderType": "RECHARGE", "clientType": "APP", "paymentMethod": "WECHAT", "paymentClient": "APP", "money": money};
|
||||
final res = await Http.post(CommonApi.addBalance, data: data);
|
||||
logger.w(res);
|
||||
final payParams = res['data'];
|
||||
logger.w(payParams);
|
||||
// 拉起支付
|
||||
await Wxsdk.payWithWx(
|
||||
appId: payParams['appid'],
|
||||
partnerId: payParams['partnerid'],
|
||||
prepayId: payParams['prepayid'],
|
||||
packageValue: payParams['package'],
|
||||
nonceStr: payParams['noncestr'],
|
||||
timestamp: int.parse(payParams['timestamp']),
|
||||
sign: payParams['sign'],
|
||||
);
|
||||
// 在回调结果中获取新的数据
|
||||
// getData(reset: true);
|
||||
}
|
||||
|
||||
/// 提现
|
||||
Future<void> withDraw({required String money}) async {
|
||||
final infoCtl = Get.find<ImUserInfoController>();
|
||||
final openId = infoCtl.customInfo['openId'];
|
||||
if (openId == null || openId.isEmpty) {
|
||||
showDialog(
|
||||
context: Get.context!,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: Text('微信授权'),
|
||||
content: const Text('余额提现至您的微信零钱内,是否前往微信授权?', style: TextStyle(fontSize: 16.0)),
|
||||
backgroundColor: Colors.white,
|
||||
surfaceTintColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)),
|
||||
elevation: 2.0,
|
||||
actionsPadding: const EdgeInsets.all(15.0),
|
||||
actions: [
|
||||
TextButton(onPressed: () => {Get.back()}, child: Text('取消', style: TextStyle(color: Colors.red))),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
//去授权
|
||||
await Wxsdk.login();
|
||||
Get.back();
|
||||
},
|
||||
child: Text('确认')),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
//
|
||||
// final res = await Http.post(CommonApi.withdraw, data: {
|
||||
// "money": money,
|
||||
// "method": "1",
|
||||
// });
|
||||
// MyDialog.('提现成功,系统将在一个工作日内将余额提现至您绑定的微信内');
|
||||
showDialog(
|
||||
context: Get.context!,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: Text('提现成功'),
|
||||
content: const Text('系统将在一个工作日内将余额提现至您绑定的微信内', style: TextStyle(fontSize: 16.0)),
|
||||
backgroundColor: Colors.white,
|
||||
surfaceTintColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)),
|
||||
elevation: 2.0,
|
||||
actionsPadding: const EdgeInsets.all(15.0),
|
||||
actions: [
|
||||
TextButton(onPressed: () => {Get.back()}, child: Text('确认')),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
getData(reset: true);
|
||||
}
|
||||
|
||||
/// 分页数据
|
||||
Future<void> getData({bool reset = false}) async {
|
||||
if (isLoading.value) return;
|
||||
if (isLoading.value) {
|
||||
logger.w('正在加载中,跳过');
|
||||
return;
|
||||
}
|
||||
|
||||
isLoading.value = true;
|
||||
logger.w('开始加载数据,reset: $reset, currentPage: $currentPage');
|
||||
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
|
||||
if (reset) {
|
||||
logger.w('重置数据');
|
||||
currentPage = 1;
|
||||
data.clear();
|
||||
hasMore.value = true;
|
||||
}
|
||||
|
||||
await Future.delayed(const Duration(seconds: 3)); // 模拟网络延迟
|
||||
|
||||
List<AccountBill> newData = List.generate(
|
||||
10,
|
||||
(index) => AccountBill(id: index),
|
||||
(index) {
|
||||
int id = currentPage * 10 + index + 1;
|
||||
// logger.w('生成数据: id=$id');
|
||||
return AccountBill(
|
||||
id: id,
|
||||
source: '来源 $currentPage-${index + 1}',
|
||||
changeAmount: (index + 1) * 10.0,
|
||||
changeType: index % 2 + 1,
|
||||
createTime: DateTime.now().toString(),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
data.addAll(newData);
|
||||
currentPage++;
|
||||
logger.w('添加了 ${newData.length} 条数据,总数据量: ${data.length}');
|
||||
|
||||
if (currentPage > 3) {
|
||||
currentPage++;
|
||||
logger.w('页码增加到: $currentPage');
|
||||
|
||||
if (currentPage > 5) {
|
||||
hasMore.value = false;
|
||||
logger.w('没有更多数据了');
|
||||
}
|
||||
|
||||
isLoading.value = false;
|
||||
logger.w('加载完成');
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import 'package:get/get.dart';
|
||||
import 'package:loopin/IM/controller/chat_controller.dart';
|
||||
import 'package:loopin/IM/im_service.dart';
|
||||
import 'package:loopin/behavior/custom_scroll_behavior.dart';
|
||||
import 'package:loopin/components/empty_tip.dart';
|
||||
import 'package:loopin/components/network_or_asset_image.dart';
|
||||
import 'package:loopin/styles/index.dart';
|
||||
import 'package:loopin/utils/index.dart';
|
||||
@ -151,7 +152,8 @@ class MutualFollowersState extends State<MutualFollowers> with SingleTickerProvi
|
||||
armedText: '释放加载',
|
||||
readyText: '加载中...',
|
||||
processingText: '加载中...',
|
||||
processedText: hasMore ? '加载完成' : '没有更多了~',
|
||||
processedText: '加载完成',
|
||||
noMoreText: '没有更多了~',
|
||||
failedText: '加载失败,请重试',
|
||||
messageText: '最后更新于 %T',
|
||||
),
|
||||
@ -161,9 +163,12 @@ class MutualFollowersState extends State<MutualFollowers> with SingleTickerProvi
|
||||
onLoad: () async {
|
||||
if (hasMore) {
|
||||
await getData();
|
||||
return hasMore ? IndicatorResult.success : IndicatorResult.noMore;
|
||||
}
|
||||
},
|
||||
childBuilder: (context, physics) {
|
||||
if (dataList.isEmpty) return EmptyTip();
|
||||
|
||||
return ListView.builder(
|
||||
physics: physics,
|
||||
itemCount: dataList.length,
|
||||
|
@ -65,10 +65,10 @@ class _NickNameState extends State<NickName> {
|
||||
border: OutlineInputBorder(),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
),
|
||||
maxLength: 20,
|
||||
maxLength: 8,
|
||||
validator: FormBuilderValidators.compose([
|
||||
FormBuilderValidators.required(errorText: '昵称不能为空'),
|
||||
FormBuilderValidators.maxLength(20, errorText: '昵称不能超过20个字符'),
|
||||
FormBuilderValidators.maxLength(8, errorText: '昵称不能超过8个字符'),
|
||||
]),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
|
@ -14,9 +14,12 @@ class Setting extends StatelessWidget {
|
||||
'onTap': () => Get.toNamed('/userInfo'),
|
||||
},
|
||||
{
|
||||
'icon': Icons.notifications,
|
||||
'title': '通知设置',
|
||||
'onTap': () => Get.toNamed('/notifications'),
|
||||
// 'icon': Icons.notifications,
|
||||
// 'title': '通知设置',
|
||||
// 'onTap': () => Get.toNamed('/notifications'),
|
||||
'icon': Icons.logout,
|
||||
'title': '注销账号',
|
||||
'onTap': () => Get.toNamed('/delete'),
|
||||
},
|
||||
{
|
||||
'icon': Icons.lock,
|
||||
|
@ -8,6 +8,7 @@ import 'package:get/get.dart';
|
||||
import 'package:image_cropper/image_cropper.dart';
|
||||
import 'package:loopin/IM/controller/im_user_info_controller.dart';
|
||||
import 'package:loopin/api/common_api.dart';
|
||||
import 'package:loopin/components/my_toast.dart';
|
||||
import 'package:loopin/components/network_or_asset_image.dart';
|
||||
import 'package:loopin/service/http.dart';
|
||||
import 'package:loopin/styles/index.dart';
|
||||
@ -68,7 +69,12 @@ class _UserInfoState extends State<UserInfo> {
|
||||
|
||||
/// 微信授权
|
||||
Future<void> wechatLogin() async {
|
||||
await Wxsdk.login();
|
||||
final isNeed = userInfoController.customInfo['openId'];
|
||||
if (isNeed == null || isNeed == '') {
|
||||
await Wxsdk.login();
|
||||
} else {
|
||||
MyToast().tip(title: '您已授权无需重复操作');
|
||||
}
|
||||
}
|
||||
|
||||
/// 选性别
|
||||
@ -239,7 +245,7 @@ class _UserInfoState extends State<UserInfo> {
|
||||
if (sizeInMB > 200) {
|
||||
MyDialog.toast('图片大小不能超过200MB', icon: const Icon(Icons.check_circle), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
||||
} else {
|
||||
print("视频合法,大小:$sizeInMB MB");
|
||||
print("图片合法,大小:$sizeInMB MB");
|
||||
//走upload(file)上传图片拿到url地址
|
||||
final istance = MyDialog.loading('上传中');
|
||||
final res = await Http.upload(CommonApi.uploadFile, filePath: file.path);
|
||||
@ -371,10 +377,6 @@ class _UserInfoState extends State<UserInfo> {
|
||||
final imageUrl = userInfoController.customInfo['coverBg'];
|
||||
return GestureDetector(
|
||||
onTap: () => pickCover(context),
|
||||
// child: Image(
|
||||
// image: (imageUrl != null && imageUrl.isNotEmpty) ? NetworkImage(imageUrl) : const AssetImage('assets/images/pic2.jpg') as ImageProvider,
|
||||
// fit: BoxFit.cover,
|
||||
// ),
|
||||
child: NetworkOrAssetImage(
|
||||
imageUrl: imageUrl,
|
||||
placeholderAsset: 'assets/images/bk.jpg',
|
||||
|
@ -4,14 +4,14 @@ import 'package:get/get.dart';
|
||||
import 'package:get/get_rx/src/rx_typedefs/rx_typedefs.dart';
|
||||
import 'package:loopin/IM/controller/chat_controller.dart';
|
||||
import 'package:loopin/IM/im_service.dart';
|
||||
import 'package:loopin/service/http.dart';
|
||||
import 'package:loopin/components/custom_sticky_header.dart';
|
||||
import 'package:loopin/api/common_api.dart';
|
||||
import 'package:loopin/api/video_api.dart';
|
||||
import 'package:loopin/utils/index.dart';
|
||||
import 'package:loopin/components/custom_sticky_header.dart';
|
||||
import 'package:loopin/components/network_or_asset_image.dart';
|
||||
import 'package:loopin/components/only_down_scroll_physics.dart';
|
||||
import 'package:loopin/service/http.dart';
|
||||
import 'package:loopin/styles/index.dart';
|
||||
import 'package:loopin/utils/index.dart';
|
||||
import 'package:nested_scroll_view_plus/nested_scroll_view_plus.dart';
|
||||
import 'package:shirne_dialog/shirne_dialog.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
||||
@ -82,7 +82,7 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
|
||||
void initState() {
|
||||
super.initState();
|
||||
args = Get.arguments ?? {};
|
||||
print('argsssssssssssssssssssssss${args}');
|
||||
print('argsssssssssssssssssssssss$args');
|
||||
itemsParams = PageParams();
|
||||
favoriteParams = PageParams();
|
||||
selfInfo();
|
||||
@ -125,44 +125,43 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// 获取用户的所有视频的点赞数量
|
||||
// 获取用户的所有视频的点赞数量
|
||||
void getUserLikesCount() async {
|
||||
try {
|
||||
final resData = await Http.get(CommonApi.accountInfo+'?memberId=${args['memberId']}');
|
||||
print('aaaaaaaaaaaaaaaaaaa${resData}');
|
||||
if(resData != null && resData['code'] == 200){
|
||||
vlogLikeCount = resData['data']['vlogLikeCount']??0;
|
||||
}
|
||||
} catch (e) {
|
||||
final resData = await Http.get('${CommonApi.accountInfo}?memberId=${args['memberId']}');
|
||||
print('aaaaaaaaaaaaaaaaaaa$resData');
|
||||
if (resData != null && resData['code'] == 200) {
|
||||
vlogLikeCount = resData['data']['vlogLikeCount'] ?? 0;
|
||||
}
|
||||
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
void loadData([int? tabIndex]) async {
|
||||
final index = tabIndex ?? currentTabIndex.value;
|
||||
if (index == 0) {
|
||||
if (itemsParams.isLoading || !itemsParams.hasMore) return;
|
||||
itemsParams.isLoading = true;
|
||||
final res = await Http.post(VideoApi.getVideoListByMemberId, data: {
|
||||
"memberId": args['memberId'],
|
||||
"current": itemsParams.page,
|
||||
"size": itemsParams.pageSize,
|
||||
});
|
||||
final obj = res['data'];
|
||||
final total = obj['total'];
|
||||
final row = obj['records'] ?? [];
|
||||
logger.i(res['data']);
|
||||
// 判断是否还有更多数据
|
||||
logger.e(items.length);
|
||||
// 添加新数据,触发响应式更新
|
||||
items.addAll(row);
|
||||
logger.e(obj);
|
||||
if (items.length >= total) {
|
||||
itemsParams.hasMore = false;
|
||||
}
|
||||
// 页码加一
|
||||
itemsParams.page++;
|
||||
//
|
||||
itemsParams.isLoading = false;
|
||||
itemsParams.isLoading = true;
|
||||
final res = await Http.post(VideoApi.getVideoListByMemberId, data: {
|
||||
"memberId": args['memberId'],
|
||||
"current": itemsParams.page,
|
||||
"size": itemsParams.pageSize,
|
||||
});
|
||||
final obj = res['data'];
|
||||
final total = obj['total'];
|
||||
final row = obj['records'] ?? [];
|
||||
logger.i(res['data']);
|
||||
// 判断是否还有更多数据
|
||||
logger.e(items.length);
|
||||
// 添加新数据,触发响应式更新
|
||||
items.addAll(row);
|
||||
logger.e(obj);
|
||||
if (items.length >= total) {
|
||||
itemsParams.hasMore = false;
|
||||
}
|
||||
// 页码加一
|
||||
itemsParams.page++;
|
||||
//
|
||||
itemsParams.isLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -370,7 +369,7 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
// 点击跳转到视频播放详情页面
|
||||
Get.toNamed('/videoDetail', arguments: {'videoId': item['id']});
|
||||
Get.toNamed('/videoDetail', arguments: {'videoId': item['id']});
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
@ -394,7 +393,7 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
fit: BoxFit.cover,
|
||||
// placeholderAsset: 'assets/images/video_placeholder.png',
|
||||
placeholderAsset: 'assets/images/bk.jpg',
|
||||
),
|
||||
// 半透明渐变底部背景
|
||||
Positioned(
|
||||
@ -421,7 +420,8 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
|
||||
bottom: 8,
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.favorite,
|
||||
const Icon(
|
||||
Icons.favorite,
|
||||
color: Colors.white,
|
||||
size: 16,
|
||||
),
|
||||
@ -457,9 +457,7 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20.0),
|
||||
child: Center(
|
||||
child: params.hasMore
|
||||
? const CircularProgressIndicator()
|
||||
: const Text('没有更多数据了'),
|
||||
child: params.hasMore ? const CircularProgressIndicator() : const Text('没有更多数据了'),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -566,7 +564,11 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
Column(children: [Text('${Utils.graceNumber(vlogLikeCount)}', style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold)), SizedBox(height: 3.0), Text('获赞')]),
|
||||
Column(children: [
|
||||
Text(Utils.graceNumber(vlogLikeCount), style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold)),
|
||||
SizedBox(height: 3.0),
|
||||
Text('获赞')
|
||||
]),
|
||||
Column(children: [
|
||||
Text('${followInfo.value.followingCount}', style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold)),
|
||||
SizedBox(height: 3.0),
|
||||
|
@ -56,7 +56,7 @@ class _UploadVideoPageState extends State<UploadVideoPage> {
|
||||
|
||||
Future<void> pickVideo() async {
|
||||
descFocusNode.unfocus();
|
||||
final hasPer = await Permissions.requestVideoPermission();
|
||||
final hasPer = await Permissions.requestPhotoPermission();
|
||||
|
||||
if (!hasPer) {
|
||||
Permissions.showPermissionDialog('相册');
|
||||
|
@ -13,16 +13,17 @@ import 'package:loopin/pages/chat/notify/noFriend.dart';
|
||||
import 'package:loopin/pages/chat/notify/system.dart';
|
||||
import 'package:loopin/pages/groupChat/groupList.dart';
|
||||
import 'package:loopin/pages/groupChat/index.dart';
|
||||
import 'package:loopin/pages/my/all_function.dart';
|
||||
import 'package:loopin/pages/my/delete.dart';
|
||||
import 'package:loopin/pages/my/des.dart';
|
||||
import 'package:loopin/pages/my/fans.dart';
|
||||
import 'package:loopin/pages/my/flowing.dart';
|
||||
import 'package:loopin/pages/my/merchant/income.dart';
|
||||
import 'package:loopin/pages/my/mutual_followers.dart';
|
||||
import 'package:loopin/pages/my/nick_name.dart';
|
||||
import 'package:loopin/pages/my/setting.dart';
|
||||
import 'package:loopin/pages/my/user_info.dart';
|
||||
import 'package:loopin/pages/my/vloger.dart';
|
||||
import 'package:loopin/pages/my/all_function.dart';
|
||||
import 'package:loopin/pages/my/merchant/income.dart';
|
||||
import 'package:loopin/pages/order/my_order.dart';
|
||||
import 'package:loopin/pages/order/seller_order.dart';
|
||||
import 'package:loopin/pages/search/index.dart';
|
||||
@ -36,9 +37,9 @@ import '../pages/auth/login.dart';
|
||||
// 商品详细
|
||||
import '../pages/goods/detail.dart';
|
||||
import '../pages/order/detail.dart';
|
||||
import '../pages/order/seller_detail.dart';
|
||||
// 订单
|
||||
import '../pages/order/index.dart';
|
||||
import '../pages/order/seller_detail.dart';
|
||||
// 引入工具类
|
||||
import '../utils/common.dart';
|
||||
|
||||
@ -68,9 +69,11 @@ final Map<String, Widget> routes = {
|
||||
'/about': const Setting(),
|
||||
'/des': const Des(),
|
||||
'/nickName': const NickName(),
|
||||
'/delete': const Delete(),
|
||||
|
||||
//通知相关
|
||||
'/noFriend': const Nofriend(),
|
||||
'/newFocus': const Newfoucs(),
|
||||
'/newFoucs': const Newfoucs(),
|
||||
'/system': const System(),
|
||||
'/interaction': const Interaction(),
|
||||
//关系链
|
||||
|
@ -2,6 +2,9 @@ import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:loopin/IM/im_service.dart';
|
||||
import 'package:loopin/controller/video_module_controller.dart';
|
||||
import 'package:loopin/utils/common.dart';
|
||||
|
||||
class HttpConfig {
|
||||
static final Dio dio = Dio(BaseOptions(
|
||||
@ -9,10 +12,10 @@ class HttpConfig {
|
||||
// baseUrl: 'http://111.62.22.190:8080',
|
||||
// baseUrl: 'http://cjh.wuzhongjie.com.cn',
|
||||
// baseUrl: 'http://82.156.121.2:8880',
|
||||
baseUrl: 'https://www.wuzhongjie.com.cn/prod-api',
|
||||
// baseUrl: 'https://www.wuzhongjie.com.cn/prod-api',
|
||||
|
||||
// baseUrl: 'http://192.168.1.65:8880',
|
||||
// baseUrl: 'http://192.168.1.22:8080',
|
||||
baseUrl: 'http://192.168.1.22:8080',
|
||||
|
||||
// connectTimeout: Duration(seconds: 30),
|
||||
// receiveTimeout: Duration(seconds: 30),
|
||||
@ -23,6 +26,24 @@ class HttpConfig {
|
||||
|
||||
static final box = GetStorage();
|
||||
|
||||
// 退出登录
|
||||
static void handleLogout() async {
|
||||
Get.offAllNamed(
|
||||
'/login',
|
||||
predicate: (route) {
|
||||
return route.settings.name == '/';
|
||||
},
|
||||
);
|
||||
final loginRes = await ImService.instance.logout();
|
||||
if (loginRes.success) {
|
||||
// 清除存储信息
|
||||
Common.logout();
|
||||
// 初始化视频
|
||||
final videoController = Get.find<VideoModuleController>();
|
||||
videoController.init();
|
||||
}
|
||||
}
|
||||
|
||||
static void init() {
|
||||
dio.interceptors.add(
|
||||
InterceptorsWrapper(
|
||||
@ -46,6 +67,26 @@ class HttpConfig {
|
||||
// logger.e(response.requestOptions.data);
|
||||
final data = response.data;
|
||||
if (data is Map<String, dynamic>) {
|
||||
// 处理token失效,强制退出
|
||||
if (data['code'] == 20006) {
|
||||
handleLogout();
|
||||
Get.snackbar(
|
||||
'登录状态已失效',
|
||||
'您当前登录状态已失效,请重新登录',
|
||||
duration: Duration(seconds: 5),
|
||||
backgroundColor: Colors.red.withAlpha(230),
|
||||
colorText: Colors.white,
|
||||
icon: const Icon(Icons.error_outline, color: Colors.white),
|
||||
);
|
||||
return handler.reject(
|
||||
DioException(
|
||||
requestOptions: response.requestOptions,
|
||||
error: data['msg'],
|
||||
response: response,
|
||||
),
|
||||
);
|
||||
}
|
||||
// 其他异常
|
||||
if (data['code'] != 200) {
|
||||
Get.snackbar(
|
||||
'错误码${data['code']}',
|
||||
|
@ -1,5 +1,8 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:audioplayers/audioplayers.dart';
|
||||
import 'package:loopin/IM/im_core.dart';
|
||||
import 'package:loopin/components/my_toast.dart';
|
||||
|
||||
class AudioPlayerService {
|
||||
static final AudioPlayerService _instance = AudioPlayerService._internal();
|
||||
@ -11,10 +14,19 @@ class AudioPlayerService {
|
||||
/// 播放本地文件
|
||||
Future<void> playLocal(String filePath) async {
|
||||
try {
|
||||
final file = File(filePath);
|
||||
if (!file.existsSync()) {
|
||||
logger.e('本地音频文件不存在:$filePath');
|
||||
MyToast().tip(title: '音频文件不存在');
|
||||
return;
|
||||
}
|
||||
await _audioPlayer.setSourceDeviceFile(filePath);
|
||||
await _audioPlayer.resume();
|
||||
// 等待播放完成
|
||||
await _audioPlayer.onPlayerComplete.first;
|
||||
} catch (e) {
|
||||
logger.e('播放本地音频失败: $e');
|
||||
MyToast().tip(title: '音频文件不存在');
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,8 +35,11 @@ class AudioPlayerService {
|
||||
try {
|
||||
await _audioPlayer.setSourceUrl(url);
|
||||
await _audioPlayer.resume();
|
||||
// 等待播放完成
|
||||
await _audioPlayer.onPlayerComplete.first;
|
||||
} catch (e) {
|
||||
logger.e('播放网络音频失败: $e');
|
||||
MyToast().tip(title: '音频文件不存在');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,4 +234,13 @@ class Utils {
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
// MERCHANT(2, "商家"),
|
||||
// AGENT(3, "代理"),
|
||||
// PLATFORM(4, "平台"),
|
||||
// REFERENCE(5, "团长")
|
||||
// 规则是将int按string去拼接
|
||||
static bool hasRole(int roleValue, int targetRole) {
|
||||
return roleValue.toString().contains(targetRole.toString());
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ class NotificationBanner {
|
||||
Get.snackbar(
|
||||
'',
|
||||
'',
|
||||
duration: const Duration(minutes: 1),
|
||||
duration: const Duration(seconds: 5),
|
||||
margin: const EdgeInsets.all(12),
|
||||
backgroundColor: FStyle.primaryColor.withAlpha(220),
|
||||
titleText: Row(
|
||||
|
@ -10,13 +10,15 @@ class VoiceService {
|
||||
VoiceService._internal();
|
||||
|
||||
final AudioRecorder _recorder = AudioRecorder();
|
||||
|
||||
String? _voiceFilePath;
|
||||
DateTime? _startTime;
|
||||
|
||||
/// 开始录音
|
||||
Future<bool> startRecording() async {
|
||||
if (await _recorder.hasPermission()) {
|
||||
final dir = await getTemporaryDirectory(); // 临时目录
|
||||
// final dir = await getTemporaryDirectory(); // 临时目录
|
||||
final dir = await getApplicationDocumentsDirectory(); // 临时目录在ios不行
|
||||
final filePath = '${dir.path}/${DateTime.now().millisecondsSinceEpoch}.m4a';
|
||||
_voiceFilePath = filePath;
|
||||
_startTime = DateTime.now();
|
||||
|
@ -5,6 +5,7 @@ import 'package:get/get.dart';
|
||||
import 'package:loopin/IM/controller/im_user_info_controller.dart';
|
||||
import 'package:loopin/IM/im_service.dart';
|
||||
import 'package:loopin/api/common_api.dart';
|
||||
import 'package:loopin/pages/my/merchant/balance/controller.dart';
|
||||
import 'package:loopin/service/http.dart';
|
||||
|
||||
class Wxsdk {
|
||||
@ -48,10 +49,28 @@ class Wxsdk {
|
||||
info.customInfo.refresh();
|
||||
logger.w(serverRes['data']['openId']);
|
||||
}
|
||||
//
|
||||
} else {
|
||||
logger.w('微信授权失败: ${res.errStr}-类型:${res.state}');
|
||||
}
|
||||
}
|
||||
// 支付
|
||||
if (res is WeChatPaymentResponse) {
|
||||
logger.e(res);
|
||||
if (res.isSuccessful) {
|
||||
logger.i("微信支付成功");
|
||||
// 获取新的数据
|
||||
final ctl = Get.find<BalanceController>();
|
||||
// 刷新记录
|
||||
ctl.getData(reset: true);
|
||||
} else {
|
||||
if (res.errCode == -2) {
|
||||
logger.w("用户取消支付");
|
||||
} else {
|
||||
logger.e("微信支付失败: code=${res.errCode}, msg=${res.errStr}");
|
||||
}
|
||||
}
|
||||
}
|
||||
// 分享
|
||||
if (res is WeChatShareResponse) {
|
||||
logger.w(res.isSuccessful);
|
||||
@ -141,4 +160,46 @@ class Wxsdk {
|
||||
);
|
||||
Fluwx().open(target: miniProgram);
|
||||
}
|
||||
|
||||
/// 微信支付
|
||||
/// [appId] 微信开放平台申请的 appId
|
||||
/// [partnerId] 商户号
|
||||
/// [prepayId] 预支付交易会话ID
|
||||
/// [packageValue] 固定值:Sign=WXPay
|
||||
/// [nonceStr] 随机字符串
|
||||
/// [timestamp] 时间戳(秒级,int 类型)
|
||||
/// [sign] 签名
|
||||
/// [signType] 非必传,默认 MD5
|
||||
/// [extData] 业务自定义
|
||||
static Future<void> payWithWx({
|
||||
required String appId,
|
||||
required String partnerId,
|
||||
required String prepayId,
|
||||
required String packageValue,
|
||||
required String nonceStr,
|
||||
required int timestamp,
|
||||
required String sign,
|
||||
String? signType,
|
||||
String? extData,
|
||||
}) async {
|
||||
try {
|
||||
final payParams = Payment(
|
||||
appId: appId,
|
||||
partnerId: partnerId,
|
||||
prepayId: prepayId,
|
||||
packageValue: packageValue,
|
||||
nonceStr: nonceStr,
|
||||
timestamp: timestamp,
|
||||
sign: sign,
|
||||
signType: signType,
|
||||
extData: extData,
|
||||
);
|
||||
logger.e(payParams.arguments);
|
||||
final result = await fluwx.pay(which: payParams);
|
||||
|
||||
logger.i("调用微信支付结果: $result");
|
||||
} catch (e) {
|
||||
logger.e("调用微信支付失败: $e");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -829,6 +829,14 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
lottie:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: lottie
|
||||
sha256: c5fa04a80a620066c15cf19cc44773e19e9b38e989ff23ea32e5903ef1015950
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.3.1"
|
||||
lpinyin:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -99,6 +99,7 @@ dependencies:
|
||||
cached_network_image: ^3.4.1
|
||||
image_cropper: ^9.1.0
|
||||
pretty_qr_code: ^3.5.0
|
||||
lottie: ^3.3.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_launcher_icons: ^0.13.1 # 使用最新版本
|
||||
@ -140,7 +141,8 @@ flutter:
|
||||
- assets/images/emotion/face05/
|
||||
#update
|
||||
- assets/images/update/rocket.png
|
||||
|
||||
- assets/animation/
|
||||
|
||||
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user