From 3b4618daed935694d36523ac458bc857d4d0e9cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=AD=90=E8=B4=A4?= Date: Mon, 2 Feb 2026 18:14:10 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/controller/discover/room_controller.dart | 46 +++++++++- lib/controller/mine/login_controller.dart | 22 +++++ lib/model/discover/task_data.dart | 4 + lib/model/mine/sys_data.dart | 18 ++++ lib/network/api_urls.dart | 3 + lib/network/user_api.dart | 4 + lib/network/user_api.g.dart | 31 +++++++ lib/pages/mine/contact_page.dart | 60 +++++++++++++ lib/pages/mine/login_page.dart | 32 +++++++ lib/widget/live/audience_item.dart | 76 ++++++++++++++-- .../live/live_room_anchor_showcase.dart | 90 ++++++++++--------- .../live/live_room_invitation_list.dart | 11 ++- 12 files changed, 345 insertions(+), 52 deletions(-) create mode 100644 lib/model/mine/sys_data.dart create mode 100644 lib/pages/mine/contact_page.dart diff --git a/lib/controller/discover/room_controller.dart b/lib/controller/discover/room_controller.dart index 5fa5511..22d4ff2 100644 --- a/lib/controller/discover/room_controller.dart +++ b/lib/controller/discover/room_controller.dart @@ -344,7 +344,7 @@ class RoomController extends GetxController with WidgetsBindingObserver { getTaskTemplateData() async { try{ final response = await _networkService.rtcApi.userGetTaskTemplateDetail( - id: "1", + id: taskData.value.taskTemplateId ?? "", ); if (response.data.isSuccess) { final data = response.data.data ?? TaskTemplateData(); @@ -1272,6 +1272,17 @@ class RoomController extends GetxController with WidgetsBindingObserver { } catch (e) { print('❌ 处理下麦消息失败: $e'); } + } else if (message['type'] == 'close_mic') { + // 处理踢人消息 + try { + final channelName = RTCManager.instance.currentChannelId; + if (channelName != null && channelName.isNotEmpty) { + await fetchRtcChannelDetail(channelName); + } + print('✅ 其他用户收到下麦消息,已刷新房间详情'); + } catch (e) { + print('❌ 处理下麦消息失败: $e'); + } } } @@ -1322,6 +1333,39 @@ class RoomController extends GetxController with WidgetsBindingObserver { return age; } + changeMic(bool mic) async { + try { + final channelId = RTCManager.instance.currentChannelId; + var res = await _networkService.rtcApi.enableRtcChannelUserAudio( + { + "channelId": channelId, + "isMicrophoneOn": mic, + "isVideoOn": true + } + ); + if(res.data.isSuccess){ + final messageData = { + 'type': 'close_mic', + 'operatorId': GlobalData().userData?.id ?? '', + 'operatorName': GlobalData().userData?.nickName ?? '', + }; + + final channelName = RTCManager.instance.currentChannelId; + if (channelName != null && channelName.isNotEmpty) { + await RTMManager.instance.publishChannelMessage( + channelName: channelName, + message: json.encode(messageData), + ); + print("关闭麦克风成功"); + } + + } + } catch(e){ + + print("关闭麦克风失败"); + } + } + inviteMic(List> selectUserId) async { try { final channelName = RTCManager.instance.currentChannelId; diff --git a/lib/controller/mine/login_controller.dart b/lib/controller/mine/login_controller.dart index 2d25f90..c1e5d97 100644 --- a/lib/controller/mine/login_controller.dart +++ b/lib/controller/mine/login_controller.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'package:dating_touchme_app/controller/global.dart'; +import 'package:dating_touchme_app/model/mine/sys_data.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:get_storage/get_storage.dart'; @@ -35,6 +36,27 @@ class LoginController extends GetxController with WidgetsBindingObserver { _userApi = Get.find(); WidgetsBinding.instance.addObserver(this); + getSysData(); + } + + final sysData = SysData().obs; + + getSysData() async { + try { + final response = await _userApi.userGetSysInfo(); + if (response.data.isSuccess && response.data.data != null) { + sysData.value = response.data.data ?? sysData(); + } else { + + // 响应失败,抛出异常 + throw Exception(response.data.message ?? '获取数据失败'); + } + } catch(e){ + print('钱包数据获取失败: $e'); + SmartDialog.showToast('钱包数据获取失败'); + rethrow; + + } } @override diff --git a/lib/model/discover/task_data.dart b/lib/model/discover/task_data.dart index 1161ebd..c0b7cd4 100644 --- a/lib/model/discover/task_data.dart +++ b/lib/model/discover/task_data.dart @@ -1,5 +1,6 @@ class TaskData { String? userTaskCompleteId; + String? taskTemplateId; int? taskGroup; int? taskType; int? stageCode; @@ -16,6 +17,7 @@ class TaskData { TaskData( {this.userTaskCompleteId, this.taskGroup, + this.taskTemplateId, this.taskType, this.stageCode, this.taskName, @@ -30,6 +32,7 @@ class TaskData { TaskData.fromJson(Map json) { userTaskCompleteId = json['userTaskCompleteId']; + taskTemplateId = json['taskTemplateId']; taskGroup = json['taskGroup']; taskType = json['taskType']; stageCode = json['stageCode']; @@ -52,6 +55,7 @@ class TaskData { Map toJson() { final Map data = new Map(); data['userTaskCompleteId'] = this.userTaskCompleteId; + data['taskTemplateId'] = this.taskTemplateId; data['taskGroup'] = this.taskGroup; data['taskType'] = this.taskType; data['stageCode'] = this.stageCode; diff --git a/lib/model/mine/sys_data.dart b/lib/model/mine/sys_data.dart new file mode 100644 index 0000000..c2780c7 --- /dev/null +++ b/lib/model/mine/sys_data.dart @@ -0,0 +1,18 @@ +class SysData { + String? customerServiceWeChat; + String? customerServicePhone; + + SysData({this.customerServiceWeChat, this.customerServicePhone}); + + SysData.fromJson(Map json) { + customerServiceWeChat = json['customerServiceWeChat']; + customerServicePhone = json['customerServicePhone']; + } + + Map toJson() { + final Map data = new Map(); + data['customerServiceWeChat'] = this.customerServiceWeChat; + data['customerServicePhone'] = this.customerServicePhone; + return data; + } +} diff --git a/lib/network/api_urls.dart b/lib/network/api_urls.dart index 8a8c563..27bd813 100644 --- a/lib/network/api_urls.dart +++ b/lib/network/api_urls.dart @@ -178,4 +178,7 @@ class ApiUrls { static const String userGetTaskTemplateDetail = 'dating-agency-mall/user/get/task-template/detail'; + + static const String userGetSysInfo = + 'dating-agency-uec/user/get/sys-info'; } diff --git a/lib/network/user_api.dart b/lib/network/user_api.dart index 9806b0d..9291283 100644 --- a/lib/network/user_api.dart +++ b/lib/network/user_api.dart @@ -12,6 +12,7 @@ import 'package:dating_touchme_app/model/mine/occupation_data.dart'; import 'package:dating_touchme_app/model/mine/payment_detail_data.dart'; import 'package:dating_touchme_app/model/mine/rose_data.dart'; import 'package:dating_touchme_app/model/mine/rose_history_data.dart'; +import 'package:dating_touchme_app/model/mine/sys_data.dart'; import 'package:dating_touchme_app/model/mine/user_base_data.dart'; import 'package:dating_touchme_app/model/mine/user_count_data.dart'; import 'package:dating_touchme_app/model/mine/user_data.dart'; @@ -291,4 +292,7 @@ abstract class UserApi { @Body() Map data, ); + + @GET(ApiUrls.userGetSysInfo) + Future>> userGetSysInfo(); } diff --git a/lib/network/user_api.g.dart b/lib/network/user_api.g.dart index 77c4005..cd04699 100644 --- a/lib/network/user_api.g.dart +++ b/lib/network/user_api.g.dart @@ -1782,6 +1782,37 @@ class _UserApi implements UserApi { return httpResponse; } + @override + Future>> userGetSysInfo() async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + const Map? _data = null; + final _options = _setStreamType>>( + Options(method: 'GET', headers: _headers, extra: _extra) + .compose( + _dio.options, + 'dating-agency-uec/user/get/sys-info', + queryParameters: queryParameters, + data: _data, + ) + .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)), + ); + final _result = await _dio.fetch>(_options); + late BaseResponse _value; + try { + _value = BaseResponse.fromJson( + _result.data!, + (json) => SysData.fromJson(json as Map), + ); + } on Object catch (e, s) { + errorLogger?.logError(e, s, _options); + rethrow; + } + final httpResponse = HttpResponse(_value, _result); + return httpResponse; + } + RequestOptions _setStreamType(RequestOptions requestOptions) { if (T != dynamic && !(requestOptions.responseType == ResponseType.bytes || diff --git a/lib/pages/mine/contact_page.dart b/lib/pages/mine/contact_page.dart new file mode 100644 index 0000000..fe630df --- /dev/null +++ b/lib/pages/mine/contact_page.dart @@ -0,0 +1,60 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:dating_touchme_app/components/page_appbar.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class ContactPage extends StatelessWidget { + String phone; + String weChat; + ContactPage({ + super.key, + this.phone = "", + this.weChat = "", + }); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PageAppbar(title: "联系方式"), + body: Container( + width: 375.w, + padding: EdgeInsets.symmetric( + vertical: 20.w, + horizontal: 15.w + ), + child: phone != "" ? Text( + "您好,人工客服热线为$phone\n工作时间9:00~21:00,请您在服务时间内拨打", + style: TextStyle( + fontSize: 14.w, + ), + ) : Column( + children: [ + Text( + "您好,请扫一扫上面的二维码,添加客服微信,工作时间9:00~21:00,请您在服务时间内联系客服", + style: TextStyle( + fontSize: 14.w + ), + ), + Expanded( + child: CachedNetworkImage( + imageUrl: weChat, + width: 345.w, + fit: BoxFit.cover, + alignment: Alignment.topCenter, + + imageBuilder: (context, imageProvider) => Container( + decoration: BoxDecoration( + image: DecorationImage( + image: imageProvider, + fit: BoxFit.fitWidth, + ), + ), + ), + ), + ) + ], + ), + ), + ); + } +} diff --git a/lib/pages/mine/login_page.dart b/lib/pages/mine/login_page.dart index 276398d..b65f0d6 100644 --- a/lib/pages/mine/login_page.dart +++ b/lib/pages/mine/login_page.dart @@ -1,4 +1,6 @@ +import 'package:dating_touchme_app/extension/ex_widget.dart'; import 'package:dating_touchme_app/generated/assets.dart'; +import 'package:dating_touchme_app/pages/mine/contact_page.dart'; import 'package:dating_touchme_app/pages/mine/open_webview.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -270,6 +272,36 @@ class LoginPage extends StatelessWidget { ], ), ), + Positioned( + top: MediaQuery.of(context).padding.top + 17.w, + right: 15.w, + child: PopupMenuButton( + tooltip: "", + padding: EdgeInsets.zero, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)), + color: Colors.white, + elevation: 8, + offset: Offset(0, 32.w), // 相对按钮下移一点 + itemBuilder: (context) => [ + if((controller.sysData.value.customerServiceWeChat != null && controller.sysData.value.customerServiceWeChat != "")) const PopupMenuItem(value: 'report', child: Text('微信联系')), + if((controller.sysData.value.customerServicePhone != null && controller.sysData.value.customerServicePhone != "")) const PopupMenuItem(value: 'block', child: Text('电话联系')), + ], + onSelected: (v) { + if (v == 'report') { + Get.to(() => ContactPage(weChat: controller.sysData.value.customerServiceWeChat ?? "",)); + } else if (v == 'block') { + Get.to(() => ContactPage(phone: controller.sysData.value.customerServicePhone ?? "",)); + } + }, + child: Text( + "联系客服", + style: TextStyle( + fontSize: 13.w, + color: const Color.fromRGBO(144, 144, 144, 1) + ), + ), // 你的小圆按钮 + ), + ) ], ), ), diff --git a/lib/widget/live/audience_item.dart b/lib/widget/live/audience_item.dart index d3a4067..b4b581b 100644 --- a/lib/widget/live/audience_item.dart +++ b/lib/widget/live/audience_item.dart @@ -1,6 +1,7 @@ import 'package:cached_network_image/cached_network_image.dart'; +import 'package:dating_touchme_app/generated/assets.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; @@ -10,7 +11,16 @@ class AudienceItem extends StatefulWidget { final Records item; final List> selectUserId; final Function(String, int) selectChange; - const AudienceItem({super.key, required this.item, required this.selectUserId, required this.selectChange}); + bool enableIf; + int nowSex; + AudienceItem({ + super.key, + required this.item, + required this.selectUserId, + required this.selectChange, + this.enableIf = false, + this.nowSex = 0, + }); @override State createState() => _AudienceItemState(); @@ -45,6 +55,7 @@ class _AudienceItemState extends State { ), SizedBox(width: 5.w,), Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( widget.item.nickName ?? "", @@ -52,12 +63,53 @@ class _AudienceItemState extends State { fontSize: 12.w ), ), - Text( - "${widget.item.age ?? ""}岁", - style: TextStyle( - fontSize: 12.w, - color: const Color.fromRGBO(121, 121, 121, 1) - ), + Row( + children: [ + Text( + "${widget.item.age ?? ""}岁", + style: TextStyle( + fontSize: 12.w, + color: const Color.fromRGBO(121, 121, 121, 1) + ), + ), + SizedBox(width: 10.w,), + if(widget.item?.genderCode == 1) Container( + width: 13.w, + height: 13.w, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(13.w)), + color: const Color.fromRGBO(255, 237, 255, 1) + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image.asset( + Assets.imagesFemale, + width: 8.w, + height: 8.w, + ), + ], + ), + ), + if(widget.item?.genderCode == 0) Container( + width: 13.w, + height: 13.w, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(13.w)), + color: const Color.fromRGBO(237, 245, 255, 1) + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image.asset( + Assets.imagesMale, + width: 8.w, + height: 8.w, + ), + ], + ), + ), + ], ), ], ) @@ -66,13 +118,21 @@ class _AudienceItemState extends State { Checkbox( value: widget.selectUserId.indexWhere((e) => e["userId"] == widget.item.userId) != -1, - onChanged: (value) { + onChanged: widget.enableIf && (widget.nowSex == widget.item.genderCode) ? null : (value) { widget.selectChange(widget.item.userId ?? "", widget.item.userManagementId ?? 0); }, activeColor: const Color.fromRGBO(117, 98, 249, 1), side: const BorderSide(color: Colors.grey), shape: const CircleBorder(), materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + fillColor: MaterialStateProperty.resolveWith((states) { + if (states.contains(MaterialState.disabled)) { + return const Color.fromRGBO(0, 0, 0, .1); // 禁用颜色 + } else if(states.contains(MaterialState.selected)){ + return const Color.fromRGBO(117, 98, 249, 1); + } + return Colors.white; + }), ), ], ), diff --git a/lib/widget/live/live_room_anchor_showcase.dart b/lib/widget/live/live_room_anchor_showcase.dart index d764406..6ea54e6 100644 --- a/lib/widget/live/live_room_anchor_showcase.dart +++ b/lib/widget/live/live_room_anchor_showcase.dart @@ -130,49 +130,55 @@ class _LiveRoomAnchorShowcaseState extends State { ), ); }), - Positioned( - left: 5.w, - bottom: 5.w, - child: Row( - children: [ - Container( - width: 20.w, - height: 20.w, - decoration: BoxDecoration( - borderRadius: BorderRadius.all( - Radius.circular(4.w), + Obx(() { + return Positioned( + left: 5.w, + bottom: 5.w, + child: Row( + children: [ + Container( + width: 20.w, + height: 20.w, + decoration: BoxDecoration( + borderRadius: BorderRadius.all( + Radius.circular(4.w), + ), + color: const Color.fromRGBO(0, 0, 0, .65), ), - color: const Color.fromRGBO(0, 0, 0, .65), - ), - child: Center( - child: Image.asset( - (_roomController - .rtcChannelDetail - .value?.anchorInfo?.isMicrophoneOn ?? false) - ? Assets.imagesMicOpen - : Assets.imagesMicClose, - width: 10.w, - height: 11.w, + child: Center( + child: Image.asset( + (_roomController + .rtcChannelDetail + .value?.anchorInfo?.isMicrophoneOn ?? false) + ? Assets.imagesMicOpen + : Assets.imagesMicClose, + width: 10.w, + height: 11.w, + ), ), - ), - ).onTap(() { - if(_roomController.currentRole == CurrentRole.broadcaster){ - _roomController - .rtcChannelDetail - .value?.anchorInfo?.isMicrophoneOn = !(_roomController - .rtcChannelDetail - .value?.anchorInfo?.isMicrophoneOn ?? false); - RTCManager.instance.muteLocalAudio(!(_roomController - .rtcChannelDetail - .value?.anchorInfo?.isMicrophoneOn ?? false)); - setState(() { + ).onTap(() { + if(_roomController.currentRole == CurrentRole.broadcaster){ + _roomController + .rtcChannelDetail + .value?.anchorInfo?.isMicrophoneOn = !(_roomController + .rtcChannelDetail + .value?.anchorInfo?.isMicrophoneOn ?? false); + RTCManager.instance.muteLocalAudio(!(_roomController + .rtcChannelDetail + .value?.anchorInfo?.isMicrophoneOn ?? false)); - }); - } - }), - ], - ), - ), + _roomController.changeMic(_roomController + .rtcChannelDetail + .value?.anchorInfo!.isMicrophoneOn ?? false); + setState(() { + + }); + } + }), + ], + ), + ); + }), ], ), SizedBox(height: 5.w), @@ -386,6 +392,8 @@ class _LiveRoomAnchorShowcaseState extends State { userInfo.isMicrophoneOn = !(userInfo.isMicrophoneOn ?? false); RTCManager.instance.muteLocalAudio(!(userInfo.isMicrophoneOn ?? false)); + + _roomController.changeMic(userInfo.isMicrophoneOn ?? false); setState(() { }); @@ -445,7 +453,7 @@ class _LiveRoomAnchorShowcaseState extends State { // return LiveRoomGuestListDialog( // initialTab: isMaleSeat ? 1 : 0, // 0: 女嘉宾, 1: 男嘉宾 // ); - return LiveRoomInvitationList(); + return LiveRoomInvitationList(nowSex: isMaleSeat ? 1 : 0,); }, ); } diff --git a/lib/widget/live/live_room_invitation_list.dart b/lib/widget/live/live_room_invitation_list.dart index 8aa9f10..9a0c2d9 100644 --- a/lib/widget/live/live_room_invitation_list.dart +++ b/lib/widget/live/live_room_invitation_list.dart @@ -13,7 +13,8 @@ import 'package:tdesign_flutter/tdesign_flutter.dart'; import 'audience_item.dart'; class LiveRoomInvitationList extends StatefulWidget { - const LiveRoomInvitationList({super.key}); + final int nowSex; + const LiveRoomInvitationList({super.key, required this.nowSex}); @override State createState() => _LiveRoomInvitationListState(); @@ -147,7 +148,13 @@ class _LiveRoomInvitationListState extends State with Ti } // 数据项 final item = _roomController.audienceList[index]; - return AudienceItem(item: item, selectUserId: selectUserId, selectChange: selectChange,); + return AudienceItem( + item: item, + selectUserId: selectUserId, + selectChange: selectChange, + enableIf: true, + nowSex: widget.nowSex, + ); }, separatorBuilder: (context, index) { // 空状态或加载状态时不显示分隔符