diff --git a/lib/constants/api.dart b/lib/constants/api.dart index 56f34b0..f92aa7e 100644 --- a/lib/constants/api.dart +++ b/lib/constants/api.dart @@ -493,4 +493,7 @@ class LiveAPI { ///发布小视频 static const String pushVideo = '/v1/live/short/publish'; + + ///直播单场详细数据 + static const String liveDataDetail = '/v1/live/live/data/info'; } diff --git a/lib/pages/live/models/live_data_detail_model.dart b/lib/pages/live/models/live_data_detail_model.dart new file mode 100644 index 0000000..b06fe23 --- /dev/null +++ b/lib/pages/live/models/live_data_detail_model.dart @@ -0,0 +1,58 @@ +class LiveDataDetailModel { + int id; + int startAt; + int endAt; + String salesVolume; + String anticipatedRevenue; + int buy; + int look; + int praise; + int fans; + + LiveDataDetailModel( + {this.id, + this.startAt, + this.endAt, + this.salesVolume, + this.anticipatedRevenue, + this.buy, + this.look, + this.praise, + this.fans}); + LiveDataDetailModel.zero() { + id = 0; + startAt = 0; + endAt = 0; + salesVolume = ''; + anticipatedRevenue = ''; + buy = 0; + look = 0; + praise = 0; + fans = 0; + } + LiveDataDetailModel.fromJson(Map json) { + id = json['id']; + startAt = json['startAt']; + endAt = json['endAt']; + salesVolume = json['salesVolume']; + anticipatedRevenue = json['anticipatedRevenue']; + buy = json['buy']; + look = json['look']; + praise = json['praise']; + fans = json['fans']; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['startAt'] = this.startAt; + data['endAt'] = this.endAt; + data['salesVolume'] = this.salesVolume; + data['anticipatedRevenue'] = this.anticipatedRevenue; + data['buy'] = this.buy; + data['look'] = this.look; + data['praise'] = this.praise; + data['fans'] = this.fans; + return data; + } +} diff --git a/lib/pages/live/sub_page/data_manager/data_manager_all_view.dart b/lib/pages/live/sub_page/data_manager/data_manager_all_view.dart index d4f38f9..c42e9d8 100644 --- a/lib/pages/live/sub_page/data_manager/data_manager_all_view.dart +++ b/lib/pages/live/sub_page/data_manager/data_manager_all_view.dart @@ -1,3 +1,4 @@ +import 'dart:math'; import 'dart:ui'; import 'package:flutter/material.dart'; @@ -15,7 +16,7 @@ class DataManagerAllView extends StatefulWidget { } class _DataManagerAllViewState extends State - with SingleTickerProviderStateMixin { + with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin { TabController _tabController; GSRefreshController _refreshController = GSRefreshController(); List titles = [ @@ -26,6 +27,7 @@ class _DataManagerAllViewState extends State '销售金额', '预计收入', ]; + int selectDay = 3; LiveTimeDataModel _dataModel = LiveTimeDataModel.zero(); @override @@ -36,6 +38,9 @@ class _DataManagerAllViewState extends State length: 6, initialIndex: 0, ); + Future.delayed(Duration(milliseconds: 300), () { + _refreshController.requestRefresh(); + }); } @override @@ -46,6 +51,7 @@ class _DataManagerAllViewState extends State @override Widget build(BuildContext context) { + super.build(context); return RefreshWidget( controller: _refreshController, onRefresh: () { @@ -87,6 +93,7 @@ class _DataManagerAllViewState extends State setState(() { selectDay = day; }); + _refreshController.requestRefresh(); }, itemBuilder: (context) { final List days = [3, 7, 15, 30]; @@ -110,7 +117,7 @@ class _DataManagerAllViewState extends State ), SizedBox(height: rSize(10)), Text( - '累计开播${_dataModel.count}场,共${DateTime.fromMillisecondsSinceEpoch(_dataModel.duration)}小时', + '累计开播${_dataModel.count}场,共${Duration(seconds: _dataModel.duration).inHours}小时', style: TextStyle( color: Color(0xFF666666), fontSize: rSP(14), @@ -166,16 +173,44 @@ class _DataManagerAllViewState extends State child: TabBarView( controller: _tabController, physics: NeverScrollableScrollPhysics(), - children: titles - .map((e) => _buildDataView( - e, - [ - DisplayScrollableList('1.1', 5), - DisplayScrollableList('1.1', 5), - DisplayScrollableList('1.1', 5), - ], - )) - .toList(), + children: [ + _buildDataView( + '收获点赞', + _dataModel.datePrise + .map((e) => DisplayScrollableList(e.date, e.count)) + .toList(), + ), + _buildDataView( + '观众人数', + _dataModel.dateLook + .map((e) => DisplayScrollableList(e.date, e.count)) + .toList(), + ), + _buildDataView( + '新增粉丝', + _dataModel.dateFans + .map((e) => DisplayScrollableList(e.date, e.count)) + .toList(), + ), + _buildDataView( + '购买人数', + _dataModel.dateBuy + .map((e) => DisplayScrollableList(e.date, e.count)) + .toList(), + ), + _buildDataView( + '销售金额', + _dataModel.dateSalesVolume + .map((e) => DisplayScrollableList(e.date, e.count)) + .toList(), + ), + _buildDataView( + '预计收入', + _dataModel.dateSalesVolume + .map((e) => DisplayScrollableList(e.date, e.count)) + .toList(), + ), + ], ), ), ), @@ -235,6 +270,11 @@ class _DataManagerAllViewState extends State } Widget _buildDataView(String title, List displayList) { + int maxHeight() { + int maxNum = displayList.map((e) => e.calcCount).reduce(max); + return maxNum == 0 ? 1 : maxNum; + } + return Padding( padding: EdgeInsets.all(rSize(10)), child: Column( @@ -266,7 +306,13 @@ class _DataManagerAllViewState extends State child: Column( children: [ Expanded( - child: Container(), + child: Align( + alignment: Alignment.bottomCenter, + child: _DataTapWidget( + model: model, + maxHeight: maxHeight(), + ), + ), ), Text( model.date, @@ -296,10 +342,92 @@ class _DataManagerAllViewState extends State else return LiveTimeDataModel.fromJson(resultData?.data['data']); } + + @override + bool get wantKeepAlive => true; } class DisplayScrollableList { String date; - int count; + dynamic count; DisplayScrollableList(this.date, this.count); + int get calcCount { + if (count is int) + return count; + else + return int.tryParse(count) ?? double.tryParse(count).toInt() ?? 0; + } +} + +class _DataTapWidget extends StatefulWidget { + final int maxHeight; + final DisplayScrollableList model; + _DataTapWidget({Key key, this.maxHeight, this.model}) : super(key: key); + + @override + __DataTapWidgetState createState() => __DataTapWidgetState(); +} + +class __DataTapWidgetState extends State<_DataTapWidget> { + bool onTap = false; + @override + Widget build(BuildContext context) { + return GestureDetector( + onLongPressStart: (detail) { + setState(() { + onTap = true; + }); + }, + onLongPressEnd: (detail) { + setState(() { + onTap = false; + }); + }, + onLongPressUp: () { + setState(() { + onTap = false; + }); + }, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + AnimatedOpacity( + opacity: onTap ? 1 : 0, + duration: Duration(milliseconds: 300), + curve: Curves.easeInOutCubic, + child: Text( + '${widget.model.count}', + style: TextStyle( + fontSize: rSP(16), + color: Color(0xFFDB2D2D), + ), + ), + ), + Container( + color: Colors.transparent, + alignment: Alignment.bottomCenter, + width: rSize(36), + child: AnimatedContainer( + curve: Curves.easeInOutCubic, + duration: Duration(milliseconds: 300), + height: rSize(6) + + rSize(90) * widget.model.calcCount / widget.maxHeight, + width: rSize(onTap ? 14 : 8), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(rSize(7)), + gradient: LinearGradient( + colors: [ + Color(0xFFC4143C).withOpacity(onTap ? 1 : 0.5), + Color(0xFFFF3457).withOpacity(onTap ? 1 : 0.5), + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + ), + ), + ], + ), + ); + } } diff --git a/lib/pages/live/sub_page/data_manager/data_manager_live_view.dart b/lib/pages/live/sub_page/data_manager/data_manager_live_view.dart index beb86c1..24b9b2d 100644 --- a/lib/pages/live/sub_page/data_manager/data_manager_live_view.dart +++ b/lib/pages/live/sub_page/data_manager/data_manager_live_view.dart @@ -1,3 +1,4 @@ +import 'package:common_utils/common_utils.dart'; import 'package:flutter/material.dart'; import 'package:recook/constants/api.dart'; import 'package:recook/constants/header.dart'; @@ -15,12 +16,28 @@ class DatamanagerLiveView extends StatefulWidget { _DatamanagerLiveViewState createState() => _DatamanagerLiveViewState(); } -class _DatamanagerLiveViewState extends State { +class _DatamanagerLiveViewState extends State + with AutomaticKeepAliveClientMixin { int _page = 1; List _dataModels = []; GSRefreshController _controller = GSRefreshController(); + @override + void initState() { + super.initState(); + Future.delayed(Duration(milliseconds: 500), () { + if (mounted) _controller.requestRefresh(); + }); + } + + @override + void dispose() { + _controller?.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { + super.build(context); return RefreshWidget( controller: _controller, onRefresh: () { @@ -42,12 +59,14 @@ class _DatamanagerLiveViewState extends State { } _buildListColumn(LiveDataListModel model) { + final date = DateTime.fromMillisecondsSinceEpoch(model.startAt * 1000); + final endDate = DateTime.fromMillisecondsSinceEpoch(model.endAt * 1000); return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - RecookDateUtil.fromMillsecond(model.startAt).prefixDay, + RecookDateUtil(date).prefixDay, style: TextStyle( color: Color(0xFF666666), fontSize: rSP(14), @@ -58,7 +77,11 @@ class _DatamanagerLiveViewState extends State { elevation: 0, color: Colors.white, onPressed: () { - CRoute.push(context, SingleDataManagerPage()); + CRoute.push( + context, + SingleDataManagerPage( + id: model.id, + )); }, padding: EdgeInsets.all(rSize(10)), shape: RoundedRectangleBorder( @@ -72,7 +95,10 @@ class _DatamanagerLiveViewState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - '09:12场', + '${DateUtil.formatDate( + date, + format: 'HH:mm', + )}场', style: TextStyle( color: Color(0xFF333333), fontSize: rSP(16), @@ -80,7 +106,13 @@ class _DatamanagerLiveViewState extends State { ), SizedBox(height: rSize(6)), Text( - '直播时间 09:02 - 10:30', + '直播时间 ${DateUtil.formatDate( + date, + format: 'HH:mm', + )} - ${DateUtil.formatDate( + endDate, + format: 'HH:mm', + )}', style: TextStyle( color: Color(0xFF333333).withOpacity(0.5), fontSize: rSP(12), @@ -113,4 +145,7 @@ class _DatamanagerLiveViewState extends State { .map((e) => LiveDataListModel.fromJson(e)) .toList(); } + + @override + bool get wantKeepAlive => true; } diff --git a/lib/pages/live/sub_page/data_manager/single_data_manager_live_page.dart b/lib/pages/live/sub_page/data_manager/single_data_manager_live_page.dart index d8b44e1..03ebbde 100644 --- a/lib/pages/live/sub_page/data_manager/single_data_manager_live_page.dart +++ b/lib/pages/live/sub_page/data_manager/single_data_manager_live_page.dart @@ -1,18 +1,40 @@ +import 'package:common_utils/common_utils.dart'; import 'package:flutter/material.dart'; +import 'package:recook/constants/api.dart'; import 'package:recook/constants/header.dart'; +import 'package:recook/manager/http_manager.dart'; +import 'package:recook/pages/live/models/live_data_detail_model.dart'; import 'package:recook/widgets/custom_painters/pie_progress_painter.dart'; import 'package:recook/widgets/recook_back_button.dart'; class SingleDataManagerPage extends StatefulWidget { - SingleDataManagerPage({Key key}) : super(key: key); + final int id; + SingleDataManagerPage({Key key, @required this.id}) : super(key: key); @override _SingleDataManagerPageState createState() => _SingleDataManagerPageState(); } class _SingleDataManagerPageState extends State { + LiveDataDetailModel model = LiveDataDetailModel.zero(); + @override + void initState() { + super.initState(); + HttpManager.post(LiveAPI.liveDataDetail, { + 'id': widget.id, + }).then((resultData) { + if (resultData?.data['data'] != null) { + setState(() { + model = LiveDataDetailModel.fromJson(resultData?.data['data']); + }); + } + }); + } + @override Widget build(BuildContext context) { + final startDate = DateTime.fromMillisecondsSinceEpoch(model.startAt * 1000); + final endDate = DateTime.fromMillisecondsSinceEpoch(model.endAt * 1000); return Scaffold( backgroundColor: Color(0xFFF9F9FB), appBar: AppBar( @@ -44,7 +66,7 @@ class _SingleDataManagerPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - '07-30 14:28场', + DateUtil.formatDate(startDate, format: 'MM-dd HH:mm场'), style: TextStyle( color: Color(0xFF333333), fontSize: rSP(16), @@ -53,7 +75,13 @@ class _SingleDataManagerPageState extends State { ), SizedBox(height: rSize(6)), Text( - '直播时间 14:28 - 17:12 共2小时44分', + '直播时间 ${DateUtil.formatDate( + startDate, + format: 'HH:mm场', + )} - ${DateUtil.formatDate( + endDate, + format: 'HH:mm场', + )} 共${endDate.difference(startDate).inHours}小时${endDate.difference(startDate).inMinutes % 59}分钟', style: TextStyle( color: Color(0xFF333333).withOpacity(0.5), fontSize: rSP(12), @@ -78,12 +106,12 @@ class _SingleDataManagerPageState extends State { childAspectRatio: 345.0 / 3.0 / (142.0 / 2.0), ), children: [ - _buildCard('1.6万', '收获点赞'), - _buildCard('1244', '观众人数'), - _buildCard('154', '新增粉丝'), - _buildCard('123', '购买人数'), - _buildCard('3.5万', '销售金额'), - _buildCard('9812', '预计收入'), + _buildCard('${model.praise}', '收获点赞'), + _buildCard('${model.look}', '观众人数'), + _buildCard('${model.fans}', '新增粉丝'), + _buildCard('${model.buy}', '购买人数'), + _buildCard('${model.salesVolume}', '销售金额'), + _buildCard('${model.anticipatedRevenue}', '预计收入'), ], shrinkWrap: true, physics: NeverScrollableScrollPhysics(), @@ -91,7 +119,7 @@ class _SingleDataManagerPageState extends State { ), ), SizedBox(height: rSize(15)), - _buildAudienceSource(), + // _buildAudienceSource(), ], ), ); diff --git a/lib/pages/live/sub_page/live_host_center_page.dart b/lib/pages/live/sub_page/live_host_center_page.dart index 6497c56..0d56bc8 100644 --- a/lib/pages/live/sub_page/live_host_center_page.dart +++ b/lib/pages/live/sub_page/live_host_center_page.dart @@ -1,7 +1,10 @@ import 'package:flutter/material.dart'; import 'package:recook/constants/api.dart'; import 'package:recook/constants/header.dart'; +import 'package:recook/manager/http_manager.dart'; import 'package:recook/manager/user_manager.dart'; +import 'package:recook/pages/live/models/live_base_info_model.dart'; +import 'package:recook/pages/live/models/live_time_data_model.dart'; import 'package:recook/pages/live/pages/goods_window_page.dart'; import 'package:recook/pages/live/sub_page/data_manager_page.dart'; import 'package:recook/utils/custom_route.dart'; @@ -9,7 +12,8 @@ import 'package:recook/widgets/recook_back_button.dart'; import 'package:recook/widgets/recook_indicator.dart'; class LiveHostCenterPage extends StatefulWidget { - LiveHostCenterPage({Key key}) : super(key: key); + final LiveBaseInfoModel model; + LiveHostCenterPage({Key key, @required this.model}) : super(key: key); @override _LiveHostCenterPageState createState() => _LiveHostCenterPageState(); @@ -91,7 +95,7 @@ class _LiveHostCenterPageState extends State ), ), Text( - '累计获赞12.6万 粉丝5.2万', + '累计获赞${widget.model.praise} 粉丝${widget.model.fans}', style: TextStyle( color: Color(0xFF999999), fontSize: rSP(14), @@ -150,9 +154,9 @@ class _LiveHostCenterPageState extends State child: TabBarView( controller: _tabController, children: [ - _buildInfoCard(), - _buildInfoCard(), - _buildInfoCard(), + _buildInfoCard(1), + _buildInfoCard(7), + _buildInfoCard(30), ], ), ), @@ -197,20 +201,41 @@ class _LiveHostCenterPageState extends State ); } - _buildInfoCard() { - return GridView( - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - gridDelegate: - SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 4), - children: [ - _buildGridChild('16万', '收获点赞'), - _buildGridChild('9075', '观众人数'), - _buildGridChild('162', '新增粉丝'), - _buildGridChild('1928', '购买人数'), - _buildGridChild('4.5万', '销售金额'), - _buildGridChild('3248', '预计收入'), - ], + Future getDataModel(int selectDay) async { + ResultData resultData = await HttpManager.post(LiveAPI.dataCount, { + 'day': selectDay, + }); + if (resultData?.data['data'] == null) + return LiveTimeDataModel.zero(); + else + return LiveTimeDataModel.fromJson(resultData?.data['data']); + } + + _buildInfoCard(int day) { + return FutureBuilder( + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + return GridView( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + gridDelegate: + SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 4), + children: [ + _buildGridChild('${snapshot.data.praise}', '收获点赞'), + _buildGridChild('${snapshot.data.look}', '观众人数'), + _buildGridChild('${snapshot.data.fans}', '新增粉丝'), + _buildGridChild('${snapshot.data.buy}', '购买人数'), + _buildGridChild('${snapshot.data.salesVolume}', '销售金额'), + _buildGridChild('${snapshot.data.anticipatedRevenue}', '预计收入'), + ], + ); + } else { + return Center( + child: CircularProgressIndicator(), + ); + } + }, + future: getDataModel(day), ); } diff --git a/lib/pages/live/sub_page/user_home/user_playback_view.dart b/lib/pages/live/sub_page/user_home/user_playback_view.dart new file mode 100644 index 0000000..eebd56a --- /dev/null +++ b/lib/pages/live/sub_page/user_home/user_playback_view.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:recook/pages/live/widget/user_live_playback_card.dart'; +import 'package:recook/widgets/refresh_widget.dart'; + +class UserPlaybackView extends StatefulWidget { + UserPlaybackView({Key key}) : super(key: key); + + @override + _UserPlaybackViewState createState() => _UserPlaybackViewState(); +} + +class _UserPlaybackViewState extends State { + GSRefreshController _controller = GSRefreshController(); + + @override + void dispose() { + _controller?.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return RefreshWidget( + controller: _controller, + onRefresh: () { + _controller.refreshCompleted(); + }, + body: ListView.builder(itemBuilder: (context, index) { + return UserPlaybackCard(); + }), + ); + } +} diff --git a/lib/pages/live/sub_page/user_home_page.dart b/lib/pages/live/sub_page/user_home_page.dart index b0ccc5c..f4767fc 100644 --- a/lib/pages/live/sub_page/user_home_page.dart +++ b/lib/pages/live/sub_page/user_home_page.dart @@ -9,6 +9,7 @@ import 'package:recook/pages/live/models/live_base_info_model.dart'; import 'package:recook/pages/live/sub_page/live_host_center_page.dart'; import 'package:recook/pages/live/sub_page/user_attention_page.dart'; import 'package:recook/pages/live/sub_page/user_home/user_activity_view.dart'; +import 'package:recook/pages/live/sub_page/user_home/user_playback_view.dart'; import 'package:recook/pages/live/widget/live_attention_button.dart'; import 'package:recook/pages/live/widget/sliver_bottom_persistent_delegate.dart'; import 'package:recook/pages/live/widget/user_live_playback_card.dart'; @@ -117,7 +118,7 @@ class _UserHomePageState extends State onPressed: () { CRoute.push( context, - LiveHostCenterPage(), + LiveHostCenterPage(model: model), ); }, ) @@ -201,9 +202,8 @@ class _UserHomePageState extends State userModel: model, initAttention: selfFlag ? true : widget.initAttention, ), - ListView.builder(itemBuilder: (context, index) { - return UserPlaybackCard(); - }) + UserPlaybackView(), + ], ), ), diff --git a/lib/pages/live/sub_page/video_page.dart b/lib/pages/live/sub_page/video_page.dart index 384a140..b4bc06f 100644 --- a/lib/pages/live/sub_page/video_page.dart +++ b/lib/pages/live/sub_page/video_page.dart @@ -23,6 +23,20 @@ class _VideoPageState extends State GSRefreshController _controller = GSRefreshController(); int _page = 1; List _videoListModels = []; + @override + void initState() { + super.initState(); + Future.delayed(Duration(milliseconds: 300), () { + if(mounted)_controller.requestRefresh(); + }); + } + + @override + void dispose() { + _controller?.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { super.build(context);