From 771e84804d0a6e16cfef05d6ffa335e02c4eb819 Mon Sep 17 00:00:00 2001 From: w-R <953969641@qq.com> Date: Sun, 31 Oct 2021 23:04:47 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/image/2x/ketang.png | Bin 0 -> 867 bytes assets/image/2x/ketang_like.png | Bin 0 -> 700 bytes assets/image/2x/ketang_message.png | Bin 0 -> 483 bytes assets/image/3x/ketang.png | Bin 0 -> 1538 bytes assets/image/3x/ketang_like.png | Bin 0 -> 1297 bytes assets/image/3x/ketang_message.png | Bin 0 -> 795 bytes assets/image/ketang.png | Bin 0 -> 495 bytes assets/image/ketang_like.png | Bin 0 -> 350 bytes assets/image/ketang_message.png | Bin 0 -> 284 bytes lib/community/community_course.dart | 234 ++++++++++++++++++ lib/community/community_page.dart | 9 +- .../community_view/class_list_view.dart | 165 ++++++++++++ .../community_view/class_title_tab.dart | 71 ++++++ .../community_view/course_banner.dart | 106 ++++++++ lib/community/community_view/home_class.dart | 204 +++++++++++++++ lib/home/home_page.dart | 1 + lib/main.dart | 2 + lib/retrofit/data/category_select_list.dart | 114 +++++++++ lib/retrofit/data/course_list.dart | 220 ++++++++++++++++ lib/retrofit/retrofit_api.dart | 22 +- lib/retrofit/retrofit_api.g.dart | 73 +++++- 21 files changed, 1214 insertions(+), 7 deletions(-) create mode 100644 assets/image/2x/ketang.png create mode 100644 assets/image/2x/ketang_like.png create mode 100644 assets/image/2x/ketang_message.png create mode 100644 assets/image/3x/ketang.png create mode 100644 assets/image/3x/ketang_like.png create mode 100644 assets/image/3x/ketang_message.png create mode 100644 assets/image/ketang.png create mode 100644 assets/image/ketang_like.png create mode 100644 assets/image/ketang_message.png create mode 100644 lib/community/community_course.dart create mode 100644 lib/community/community_view/class_list_view.dart create mode 100644 lib/community/community_view/class_title_tab.dart create mode 100644 lib/community/community_view/course_banner.dart create mode 100644 lib/community/community_view/home_class.dart create mode 100644 lib/retrofit/data/category_select_list.dart create mode 100644 lib/retrofit/data/course_list.dart diff --git a/assets/image/2x/ketang.png b/assets/image/2x/ketang.png new file mode 100644 index 0000000000000000000000000000000000000000..c65f49568aeff753f783fcaef9119a900ffd7ede GIT binary patch literal 867 zcmV-p1DyPcP)Px&9Z5t%RA@u(m|aLyaTvy*|Jh7j1QitBSl$(7);0}-z>qKsA}I1AnmfxVB=APM ztq4K7&`arqJlnAkO47R=Vnh%v%*~8VlccLm6GQx*+uC_!rxqrUi**O`B)fFhI;TYSP{s& z07xpHiOi!d3UeuEGtb_-TMESa}Gm22t^!V~+sxr4+?D2BKR4wj}BpSQ8+PE$@+t zMNJad-B;6^@;RLLw@NB8)(4JKX6Ae#W(L^M%*?oTq^>KHE-jnL^N=9ObqpN=r~7QW z9@Pn~)@Z+bIgj$AiQgS10OBAV3Ao(vG8JMIcrO@^%M2W<{MGLEOnaBssZPM=;ctL& zQx?F`?035Bl?bqV96O0jK>(WwG?B+>Z?9AQp=8GI099;uf!|LI3HLXEu@SbIcffz; zp5jj~yW3SupqAwQ6X4w!l*&;BU=#S~U^oLnGI6ZpeYKF;;(ob00<($yD-g;Phe%@k z`Y#HK)*$KtM4o37t^;)6&wCn`y0*0bpuiV#9BXGpbBh+ssZdpmqQ3yD0JI3!yVn5F t+fk+Sj0BWArgy!OfZmQOotN_o`~p58lt9zVDtrI{002ovPDHLkV1iNsj?n-B literal 0 HcmV?d00001 diff --git a/assets/image/2x/ketang_like.png b/assets/image/2x/ketang_like.png new file mode 100644 index 0000000000000000000000000000000000000000..e9b868037b37fb237a9ba10867de486141778271 GIT binary patch literal 700 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 ZfnQ$5RX~Ls4Uzx=002ovPDHLkV1oR8V%Y!y literal 0 HcmV?d00001 diff --git a/assets/image/ketang.png b/assets/image/ketang.png new file mode 100644 index 0000000000000000000000000000000000000000..e3ed6ce48db6a61bf32550704a1a7ef6074a31fd GIT binary patch literal 495 zcmVPx$sYygZR7gwhl+P;!VHn51?~tu^*ph=3yAFGhQoFcNlvVr#6vggj9XR^~l!M&J z#jav(Qn+XjA_q=Vnw?qODk0&p7A4`b&CVlRG}xK7!yc6P_Vzx{^Z7nM-iM*UhZQ(J z{sB2F{G~+h5Z{S3Rp<1~Vn8Y-rNPCp_UQwlA<%78E!KMmX1X_|q#Tle`Qi=o3D8?k z7R#*}_3u7KiZjx`beb2S##V{6n#Sd0GB?`{V4`&x9(yA;&-XDG4dcoPWCk$VHi8aE zQ%1d_rm=ChhhQ{}#Pi+PjD8AujYvdTif>^`T={*#LE;Fr@;c<3lkEL4lfh_HADm@X z2+HdalgGx5HUr2#IvR_D0~Ey>@$38pjDd4Gya9mOHJX}1cmg1PSOJV`09H%DE(4`9 z%?Ed#vOW#Pd)6}d*JHR6-1B@pakNc@E}0j$wSc+>UquNJXTVH^7sm9;s0*kM^_Lc_ zMTZ3JNIt=zEr3h#4gi?rh&7755X)wx3vdZu4+l04Ye|dP6MWE9=w9@#l8Ttb3*&hK l@|qk2W7BK-y8`|`-~}nJz8MaLIPL%d002ovPDHLkV1h9{+nxXb literal 0 HcmV?d00001 diff --git a/assets/image/ketang_like.png b/assets/image/ketang_like.png new file mode 100644 index 0000000000000000000000000000000000000000..2dcc4f9ebeae0cfb74cbce44274662809f9d24d2 GIT binary patch literal 350 zcmV-k0iphhP)Px$7)eAyR5(wqlc7rlQ4oi}&n{-Iip^k9ylC`4uqg%+EQSNmViG*jE{LFDu$rtU zwHU-KCPk}Y6i42%yR3J+uy9U!@ZQWf^L{fULz+Xz^9K+SbHD~L1-t|2(DPXV#(`(x zSbfM77l4RZ1@?dg;1QSvwll{r@B++ccOCU2z4ZVQk?2oAOHEmN77?TAP`^rd)4(k- zqkiOobznhVYobX+oB{XhNe<`$W9m*rz&`M)c5}cIu&cHj0`8Jt>U9nnNhJYSYNFR# zM6`irU|#(xcVPm!0Z!G!DndkT0h_?0`dW0Mq;M^yDtG{pwQc}X7hDPGgIogN*(4Ta wm%mrL3)yfT0ZHJt`c|dO|1MLL8R+-<1+A@b5pt6!5C8xG07*qoM6N<$f{{Fq8vpPx#)=5M`R5(xV(?3dsK@f)V=kZSrTBH$d)7j(zp1{f*cm+wDMhnq9*xGml4`Gwe zODKp4BGEuz*&y<{@EJ9&+iWxQo7vf2S#r5#KA%966W|!=EI{9YbM>smHGv5*0PYq5 zJHV~lt$_et_4PX-$p$b{=f#^H`~%4h*i_4Hw5}v&`Y-BAJ;WQ}8F;L+AAk{X3haKo zlPz_x2715&uwDWCKp)r!u7Ppc)u%5&ThB>KtCzqbaHL)|V}JA)lB@z(z`Z*BHBb_g ilwxb@^iM$B5#Ir5oL>>5E{sS30000 createState() { + return _CommunityCourse(); + } +} + +class _CommunityCourse extends State + with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin { + final ScrollController scrollController = ScrollController(); + final RefreshController refreshController = RefreshController(); + + ApiService apiService; + List brands = []; + BrandData brandData; + List globaKeys = []; + List bannerData = []; + List classSelectList = []; + List classList = []; + List classTabList = []; + int checkIndex = 0; + + @override + void initState() { + super.initState(); + + eventBus.on().listen((event) { + print("object: CommunityCourse"); + if (event.type < 3) { + setState(() {}); + } + }); + classListAsync(); + } + + ///课程分类列表 + classListAsync() async { + if (apiService == null) { + SharedPreferences value = await SharedPreferences.getInstance(); + apiService = ApiService( + Dio(), + context: context, + token: value.getString("token"), + ); + } + BaseData> baseData = await apiService.categoryList().catchError((onError) {}); + if (baseData != null && baseData.isSuccess) { + setState(() { + classSelectList = baseData.data; + }); + queryClassList(""); + } + EasyLoading.dismiss(); + } + + ///课程列表 + queryClassList(categoryId) async { + if (apiService == null) { + SharedPreferences value = await SharedPreferences.getInstance(); + apiService = ApiService( + Dio(), + context: context, + token: value.getString("token"), + ); + } + BaseData> baseData = await apiService.courseList({ + "categoryId":categoryId, + "pageNum": 1, + "pageSize":10, + "searchKey": "", + "state":0 + }).catchError((error) { + if(categoryId == ""){ + refreshController.refreshFailed(); + refreshController.loadFailed(); + } + }); + + if (baseData.isSuccess) { + if(categoryId == "") { + refreshController.refreshCompleted(); + refreshController.loadComplete(); + setState(() { + classList.clear(); + classList.addAll(baseData.data.list); + }); + if(classSelectList.length > 0) + queryClassList(classSelectList[checkIndex].id); + }else{ + setState(() { + classTabList.clear(); + classTabList.addAll(baseData.data.list); + }); + } + } + } + + _onRefresh(){ + classListAsync(); + } + + @override + Widget build(BuildContext context) { + super.build(context); + return + Stack( + children: [ + Positioned( + child: Container( + child: SmartRefresher( + controller: refreshController, + enablePullDown: true, + enablePullUp: false, + header: MyHeader(), + physics: ClampingScrollPhysics(), + onRefresh: _onRefresh, + scrollController: scrollController, + child: Container( + child: SingleChildScrollView( + physics: NeverScrollableScrollPhysics(), + child: Container( + color: Color(0xFFF7F7F7), + margin: EdgeInsets.only(top: 16.h), + child: Column( + children: classChildItem(), + ), + ), + ), + ), + ), + ), + bottom:0, + top: 0, + left: 0, + right: 0, + ), + if (brands != null && brands.length > 0) + Positioned( + child: Container( + color: Colors.white, + child: StoreTitleTab( + brands, + globaKeys, + scrollController, + isScroll: true, + ), + ), + top: 0, + left: 0, + right: 0, + ), + ], + ); + } + + List classChildItem() { + var widgets = [ + ///课程banner + CourseBanner(bannerData), + + SizedBox(height: 28), + + ///回乡小课堂 + HomeClass(classList), + + SizedBox(height: 28), + + ///课程导航栏 + ClassTitleTab(classSelectList,(index){ + checkIndex = index; + queryClassList(classSelectList[index].id); + }), + + ///课程列表 + ClassListView(classTabList), + + ]; + + // if (brands == null) return widgets; + // brands.forEach((value) { + // widgets.add( + // Container( + // key: globaKeys[brands.indexOf(value)], + // child: Container( + // child: Html( + // data: value.content, + // customImageRenders: { + // assetUriMatcher(): assetImageRender(), + // networkSourceMatcher(extension: "svg"): svgNetworkImageRender(), + // networkSourceMatcher(): networkImageRender( + // loadingWidget: () { + // return Container(); + // }, + // ), + // }, + // ), + // ), + // ), + // ); + // }); + return widgets; + } + + + @override + bool get wantKeepAlive => true; +} diff --git a/lib/community/community_page.dart b/lib/community/community_page.dart index 4c9c9b9e..a67e7df8 100644 --- a/lib/community/community_page.dart +++ b/lib/community/community_page.dart @@ -7,6 +7,8 @@ import 'package:huixiang/view_widget/my_appbar.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:huixiang/view_widget/my_tab.dart'; +import 'community_course.dart'; + class CommunityPage extends StatefulWidget { @override State createState() { @@ -22,8 +24,8 @@ class _CommunityPage extends State "关注", "推荐", "头条", + "课程", "关于我们", - // "课程", // "直播", ]; @@ -96,7 +98,10 @@ class _CommunityPage extends State return BrandPage(); }else if(e == "头条"){ return HotArticlePage(); - } else { + }else if(e == "课程"){ + return CommunityCourse(); + } + else { return CommunityChildPage(e); } }).toList(), diff --git a/lib/community/community_view/class_list_view.dart b/lib/community/community_view/class_list_view.dart new file mode 100644 index 00000000..3879a322 --- /dev/null +++ b/lib/community/community_view/class_list_view.dart @@ -0,0 +1,165 @@ +import 'package:flutter/material.dart'; +import 'package:huixiang/retrofit/data/course_list.dart'; +import 'package:huixiang/utils/flutter_utils.dart'; +import 'package:huixiang/utils/font_weight.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:huixiang/view_widget/custom_image.dart'; + +class ClassListView extends StatefulWidget { + final List classList; + + ClassListView(this.classList); + + @override + State createState() { + return _ClassListView(); + } +} + +class _ClassListView extends State { + @override + Widget build(BuildContext context) { + return GridView.builder( + itemCount:widget.classList == null ? 0 : widget.classList.length, + padding: EdgeInsets.only( + left: 16.w, + right: 16.w, + top: 13.h, + bottom: 16.h, + ), + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + //一行的Widget数量 + crossAxisCount: 3, + //水平子Widget之间间距 + crossAxisSpacing: 11.w, + //垂直子Widget之间间距 + mainAxisSpacing: 16.w, + //子Widget宽高比例 0.59 + childAspectRatio: + 166 / (281 / 2 + (281 / 2) * AppUtils.textScale(context)), + ), + itemBuilder: (context, index) { + return GestureDetector( + onTap: () { + Navigator.of(context).pushNamed('/router/class_details',arguments: {"id": widget.classList[index].id}); + }, + child: classListItem(widget.classList[index]), + ); + }, + ); + } + + Widget classListItem(CourseList classList) { + return Container( + width: 106, + height: 189, + margin: EdgeInsets.symmetric( + // horizontal: 6.w, + vertical: 3, + ), + color: Colors.white, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( + alignment: Alignment(0.9, 0.9), + children: [ + Container( + color: Color.fromARGB(80, 0, 0, 0), + child: ClipRRect( + child: MImage( + classList.coverImg, + width: double.infinity, + height: 120, + fit: BoxFit.cover, + errorSrc: "assets/image/default_1.png", + fadeSrc: "assets/image/default_1.png", + ), + borderRadius: BorderRadius.vertical( + top: Radius.circular(4), + ), + ), + ), + Container( + padding: EdgeInsets.only(left: 4), + child: Row( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.asset( + "assets/image/ketang_like.png", + width: 16.w, + height: 16.h, + color: Colors.white, + ), + SizedBox(width: 5), + Text( + classList.likes.toString(), + style: TextStyle( + fontSize: 12.sp, + fontWeight: MyFontWeight.regular, + color: Colors.white, + ), + ), + ], + ), + SizedBox(width: 8), + Row( + children: [ + Image.asset( + "assets/image/ketang_message.png", + width: 16.w, + height: 16.h, + color: Colors.white, + ), + SizedBox(width: 5), + Text( + classList.viewers.toString(), + style: TextStyle( + fontSize: 12.sp, + fontWeight: MyFontWeight.regular, + color: Colors.white, + ), + ), + ], + ), + ], + ), + ), + ], + ), + Container( + padding: EdgeInsets.only(left: 4, top: 3), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + classList.subject, + style: TextStyle( + fontSize: 14.sp, + fontWeight: MyFontWeight.medium, + color: Colors.black, + ), + ), + SizedBox(height: 2), + Text( + "讲师:${classList.author.name}", + style: TextStyle( + fontSize: 12.sp, + fontWeight: MyFontWeight.regular, + color: Colors.black, + ), + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/community/community_view/class_title_tab.dart b/lib/community/community_view/class_title_tab.dart new file mode 100644 index 00000000..44f47df3 --- /dev/null +++ b/lib/community/community_view/class_title_tab.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; +import 'package:huixiang/generated/l10n.dart'; +import 'package:huixiang/retrofit/data/category_select_list.dart'; +import 'package:huixiang/retrofit/data/goods_category.dart'; +import 'package:huixiang/utils/font_weight.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:huixiang/view_widget/my_tab.dart'; + +class ClassTitleTab extends StatefulWidget { + final List classSelectList; + final Function notifyClassSelectList; + + ClassTitleTab(this.classSelectList,this.notifyClassSelectList); + + @override + State createState() { + return _ClassTitleTab(); + } +} + +class _ClassTitleTab extends State + with SingleTickerProviderStateMixin { + TabController tabController; + + @override + void initState() { + super.initState(); + if (widget.classSelectList != null && widget.classSelectList.length > 0) + tabController = TabController(length: widget.classSelectList.length, vsync: this ); + } + + @override + Widget build(BuildContext context) { + return Container( + alignment: Alignment.centerLeft, + child: DefaultTabController( + length:widget.classSelectList == null + ? 0 + : widget.classSelectList.length, + child: TabBar( + isScrollable: true, + //可滚动 + indicatorColor: Color(0xff39B54A), + labelColor: Color(0xff32A060), + labelStyle: TextStyle( + fontSize: 14.sp, + fontWeight: FontWeight.bold, + ), + unselectedLabelStyle: TextStyle( + fontSize: 14.sp, + fontWeight: MyFontWeight.regular, + ), + controller: tabController, + //未选中文字颜色 + unselectedLabelColor: Color(0xff4D4D4D), + indicatorSize: TabBarIndicatorSize.label, + onTap: (index){ + widget.notifyClassSelectList(index); + }, + //指示器与文字等宽 + tabs:widget.classSelectList == null + ? [] + : widget.classSelectList + .map((e) => MyTab(text: e.name)) + .toList(), + ), + ), + ); + } + +} diff --git a/lib/community/community_view/course_banner.dart b/lib/community/community_view/course_banner.dart new file mode 100644 index 00000000..fc3078bb --- /dev/null +++ b/lib/community/community_view/course_banner.dart @@ -0,0 +1,106 @@ + +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter_swiper/flutter_swiper.dart'; +import 'package:huixiang/retrofit/data/banner.dart'; +import 'package:huixiang/view_widget/custom_image.dart'; + +class CourseBanner extends StatefulWidget { + + final List bannerData; + + CourseBanner(this.bannerData); + + @override + State createState() { + return _CourseBanner(); + } + +} + +class _CourseBanner extends State { + @override + Widget build(BuildContext context) { + return Container( + child: AspectRatio( + aspectRatio: 2.08, + child: Swiper( + viewportFraction: 0.88, + scale: 0.93, + pagination: SwiperPagination( + alignment: Alignment.bottomCenter, + builder: DotSwiperPaginationBuilder( + size: 8, + activeSize: 8, + space: 5, + activeColor: Colors.black, + color: Colors.black.withAlpha(76), + ), + ), + physics: BouncingScrollPhysics(), + itemBuilder: (context, position) { + return InkWell( + onTap: () { + bannerClick(widget.bannerData[position]); + }, + child: MImage( + (widget.bannerData != null && position < widget.bannerData.length) + ? widget.bannerData[position].imgUrl + : "", + fit: BoxFit.cover, + radius: BorderRadius.circular(8), + errorSrc: "assets/image/default_2_1.png", + fadeSrc: "assets/image/default_2_1.png", + ), + ); + }, + itemCount: (widget.bannerData != null && widget.bannerData.length > 0) + ? widget.bannerData.length + : 1), + ), + ); + } + + /// contentType 跳转类型(0:不跳转,1:积分商品,2:活动,3:文章) + bannerClick(BannerData bannerData) async { + switch (bannerData.contentType) { + case 1: + Navigator.of(context).pushNamed('/router/integral_store_page', + arguments: {"goodsId": bannerData.content}); + break; + case 2: + Navigator.of(context) + .pushNamed('/router/web_page', arguments: { + "activityId": bannerData.content, + }); + break; + case 3: + Navigator.of(context) + .pushNamed('/router/web_page', arguments: { + "articleId": bannerData.content, + }); + break; + case 4: + String router = bannerData.content; + if (router.contains("?")) { + String params = router.substring(router.indexOf("?")); + params = params.replaceAll("?", ""); + Map map = jsonDecode(params); + Navigator.of(context).pushNamed(router, arguments: map); + } else { + Navigator.of(context).pushNamed(router); + } + break; + } + } + + + +} + + + + + + diff --git a/lib/community/community_view/home_class.dart b/lib/community/community_view/home_class.dart new file mode 100644 index 00000000..2f4c13ad --- /dev/null +++ b/lib/community/community_view/home_class.dart @@ -0,0 +1,204 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_baidu_mapapi_base/flutter_baidu_mapapi_base.dart'; +import 'package:flutter_baidu_mapapi_map/flutter_baidu_mapapi_map.dart'; +import 'package:huixiang/retrofit/data/course_list.dart'; +import 'package:huixiang/retrofit/retrofit_api.dart'; +import 'package:huixiang/utils/font_weight.dart'; +import 'package:huixiang/view_widget/custom_image.dart'; +import 'package:huixiang/view_widget/item_title.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class HomeClass extends StatefulWidget { + final List classList; + + HomeClass(this.classList); + @override + State createState() { + return _HomeClass(); + } +} + +class _HomeClass extends State { + ApiService apiService; + BMFCoordinate latLng; + + BMFMapController _mapController; + final TextEditingController editingController = TextEditingController(); + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Column( + children: [ + ItemTitle( + text: "回乡小课堂", + imgPath: "assets/image/ketang.png", + ), + Container( + height: 189, + margin: EdgeInsets.only(top:10), + child: ListView.builder( + scrollDirection: Axis.horizontal, + physics: BouncingScrollPhysics(), + padding: EdgeInsets.symmetric(horizontal: 10), + itemCount:widget.classList == null ? 0 : widget.classList.length, + itemBuilder: (context, position) { + return GestureDetector( + onTap: () { + Navigator.of(context).pushNamed('/router/class_details'); + }, + child: classItem(widget.classList[position]), + ); + }, + ), + ), + ], + ); + } + + Widget classItem(CourseList classList) { + return Container( + width: 168, + height: 189, + margin: EdgeInsets.symmetric( + horizontal: 6.w, + vertical: 3, + ), + color: Colors.white, + child: Column( + children: [ + Stack( + alignment: Alignment(0.9,0.9), + children: [ + Container( + color: Color.fromARGB(80, 0, 0, 0), + child: ClipRRect( + child: MImage( + classList.coverImg, + width: double.infinity, + height: 120, + fit: BoxFit.cover, + errorSrc: "assets/image/default_1.png", + fadeSrc: "assets/image/default_1.png", + ), + borderRadius: BorderRadius.vertical( + top: Radius.circular(4), + ), + ), + ), + Container( + padding: EdgeInsets.only(left: 4), + child:Row( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.asset( + "assets/image/ketang_like.png", + width: 16.w, + height: 16.h, + color: Colors.white, + ), + SizedBox(width:5), + Text( + classList.likes.toString(), + style: TextStyle( + fontSize: 12.sp, + fontWeight: MyFontWeight.regular, + color: Colors.white, + ), + ), + ], + ), + SizedBox(width:8), + Row( + children: [ + Image.asset( + "assets/image/ketang_message.png", + width: 16.w, + height: 16.h, + color: Colors.white, + ), + SizedBox(width:5), + Text( + classList.viewers.toString(), + style: TextStyle( + fontSize: 12.sp, + fontWeight: MyFontWeight.regular, + color: Colors.white, + ), + ), + ], + ), + ], + ), + ), + ], + ), + Container( + padding: EdgeInsets.only(left: 3,top: 2), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height:2), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + height: 15.h, + width: 30.w, + alignment: Alignment.center, + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular(2), + border: Border.all( + width: 1, + color: Color(0xFFFF7A1A), + style: BorderStyle.solid, + ), + ), + child: Text( + ( classList?.tags != null && classList.tags.length > 0 )?classList.tags[0] : "", + style: TextStyle( + fontSize: 12.sp, + fontWeight: MyFontWeight.medium, + color: Color(0xFFFF7A1A), + ), + ), + ), + SizedBox(width:5), + Expanded(child: Text( + classList.subject, + style: TextStyle( + fontSize: 14.sp, + fontWeight: MyFontWeight.medium, + color: Colors.black, + ), + ),), + ], + ), + SizedBox(height:3), + Text( + "讲师:${classList.author.name}", + style: TextStyle( + fontSize: 12.sp, + fontWeight: MyFontWeight.regular, + color: Colors.black, + ), + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/home/home_page.dart b/lib/home/home_page.dart index e8d60d7e..0ff917ce 100644 --- a/lib/home/home_page.dart +++ b/lib/home/home_page.dart @@ -284,6 +284,7 @@ class _HomePage extends State with AutomaticKeepAliveClientMixin { ///积分商城 HomeIntegralStore(gooods, callback), + ///精选活动 FeaturedActivity(), ///积分商品头Tab diff --git a/lib/main.dart b/lib/main.dart index fbdd0f24..02623816 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -70,6 +70,7 @@ import 'package:tpns_flutter_plugin/android/xg_android_api.dart'; import 'package:tpns_flutter_plugin/tpns_flutter_plugin.dart'; import 'package:flutter_baidu_mapapi_base/flutter_baidu_mapapi_base.dart'; +import 'community/community_view/class_details.dart'; import 'home/guide_page.dart'; import 'home/home_view/activity_list.dart'; import 'main_page.dart'; @@ -307,4 +308,5 @@ Map routers = { '/router/communityFollow': (context, {arguments}) => CommunityFollow(), '/router/releasePage': (context, {arguments}) => ReleasePage(), '/router/activity_list': (context, {arguments}) => ActivityList(), + '/router/class_details': (context, {arguments}) => ClassDetails(), }; diff --git a/lib/retrofit/data/category_select_list.dart b/lib/retrofit/data/category_select_list.dart new file mode 100644 index 00000000..d6309a6e --- /dev/null +++ b/lib/retrofit/data/category_select_list.dart @@ -0,0 +1,114 @@ +/// id : "1452472226421932032" +/// createTime : "2021-10-25 11:08:59" +/// createUser : "1" +/// updateTime : "2021-10-25 11:08:59" +/// updateUser : "1" +/// name : "4444" +/// isTop : true +/// sort : 0 +/// isDelete : 0 + +class CategorySelectList { + CategorySelectList({ + String id, + String createTime, + String createUser, + String updateTime, + String updateUser, + String name, + bool isTop, + int sort, + int isDelete,}){ + _id = id; + _createTime = createTime; + _createUser = createUser; + _updateTime = updateTime; + _updateUser = updateUser; + _name = name; + _isTop = isTop; + _sort = sort; + _isDelete = isDelete; +} + + CategorySelectList.fromJson(dynamic json) { + _id = json['id']; + _createTime = json['createTime']; + _createUser = json['createUser']; + _updateTime = json['updateTime']; + _updateUser = json['updateUser']; + _name = json['name']; + _isTop = json['isTop']; + _sort = json['sort']; + _isDelete = json['isDelete']; + } + String _id; + String _createTime; + String _createUser; + String _updateTime; + String _updateUser; + String _name; + bool _isTop; + int _sort; + int _isDelete; + + String get id => _id; + String get createTime => _createTime; + String get createUser => _createUser; + String get updateTime => _updateTime; + String get updateUser => _updateUser; + String get name => _name; + bool get isTop => _isTop; + int get sort => _sort; + int get isDelete => _isDelete; + + + set id(String value) { + _id = value; + } + + Map toJson() { + final map = {}; + map['id'] = _id; + map['createTime'] = _createTime; + map['createUser'] = _createUser; + map['updateTime'] = _updateTime; + map['updateUser'] = _updateUser; + map['name'] = _name; + map['isTop'] = _isTop; + map['sort'] = _sort; + map['isDelete'] = _isDelete; + return map; + } + + set createTime(String value) { + _createTime = value; + } + + set createUser(String value) { + _createUser = value; + } + + set updateTime(String value) { + _updateTime = value; + } + + set updateUser(String value) { + _updateUser = value; + } + + set name(String value) { + _name = value; + } + + set isTop(bool value) { + _isTop = value; + } + + set sort(int value) { + _sort = value; + } + + set isDelete(int value) { + _isDelete = value; + } +} \ No newline at end of file diff --git a/lib/retrofit/data/course_list.dart b/lib/retrofit/data/course_list.dart new file mode 100644 index 00000000..3f9142fa --- /dev/null +++ b/lib/retrofit/data/course_list.dart @@ -0,0 +1,220 @@ +/// id : "1452470112895369216" +/// createTime : "2021-10-25 11:00:35" +/// createUser : "1" +/// updateTime : "2021-10-25 11:00:35" +/// updateUser : "1" +/// categoryId : "1452453250065235968" +/// subject : "11" +/// tags : ["11"] +/// coverImg : "https://pos.upload.gznl.top/0000/2021/10/61dda29b-19cf-4d47-b6c7-c07f895beeaa.jpg" +/// author : {"name":"11","avatar":"11"} +/// introduce : "11" +/// viewers : 0 +/// likes : 0 +/// state : true +/// isDelete : 0 + +class CourseList { + CourseList({ + String id, + String createTime, + String createUser, + String updateTime, + String updateUser, + String categoryId, + String subject, + List tags, + String coverImg, + Author author, + String introduce, + int viewers, + int likes, + bool state, + int isDelete,}){ + _id = id; + _createTime = createTime; + _createUser = createUser; + _updateTime = updateTime; + _updateUser = updateUser; + _categoryId = categoryId; + _subject = subject; + _tags = tags; + _coverImg = coverImg; + _author = author; + _introduce = introduce; + _viewers = viewers; + _likes = likes; + _state = state; + _isDelete = isDelete; +} + + CourseList.fromJson(dynamic json) { + _id = json['id']; + _createTime = json['createTime']; + _createUser = json['createUser']; + _updateTime = json['updateTime']; + _updateUser = json['updateUser']; + _categoryId = json['categoryId']; + _subject = json['subject']; + _tags = json['tags'] != null ? json['tags'].cast() : []; + _coverImg = json['coverImg']; + _author = json['author'] != null ? Author.fromJson(json['author']) : null; + _introduce = json['introduce']; + _viewers = json['viewers']; + _likes = json['likes']; + _state = json['state']; + _isDelete = json['isDelete']; + } + String _id; + String _createTime; + String _createUser; + String _updateTime; + String _updateUser; + String _categoryId; + String _subject; + List _tags; + String _coverImg; + Author _author; + String _introduce; + int _viewers; + int _likes; + bool _state; + int _isDelete; + + String get id => _id; + String get createTime => _createTime; + String get createUser => _createUser; + String get updateTime => _updateTime; + String get updateUser => _updateUser; + String get categoryId => _categoryId; + String get subject => _subject; + List get tags => _tags; + String get coverImg => _coverImg; + Author get author => _author; + String get introduce => _introduce; + int get viewers => _viewers; + int get likes => _likes; + bool get state => _state; + int get isDelete => _isDelete; + + + set id(String value) { + _id = value; + } + + Map toJson() { + final map = {}; + map['id'] = _id; + map['createTime'] = _createTime; + map['createUser'] = _createUser; + map['updateTime'] = _updateTime; + map['updateUser'] = _updateUser; + map['categoryId'] = _categoryId; + map['subject'] = _subject; + map['tags'] = _tags; + map['coverImg'] = _coverImg; + if (_author != null) { + map['author'] = _author.toJson(); + } + map['introduce'] = _introduce; + map['viewers'] = _viewers; + map['likes'] = _likes; + map['state'] = _state; + map['isDelete'] = _isDelete; + return map; + } + + set createTime(String value) { + _createTime = value; + } + + set createUser(String value) { + _createUser = value; + } + + set updateTime(String value) { + _updateTime = value; + } + + set updateUser(String value) { + _updateUser = value; + } + + set categoryId(String value) { + _categoryId = value; + } + + set subject(String value) { + _subject = value; + } + + set tags(List value) { + _tags = value; + } + + set coverImg(String value) { + _coverImg = value; + } + + set author(Author value) { + _author = value; + } + + set introduce(String value) { + _introduce = value; + } + + set viewers(int value) { + _viewers = value; + } + + set likes(int value) { + _likes = value; + } + + set state(bool value) { + _state = value; + } + + set isDelete(int value) { + _isDelete = value; + } +} + +/// name : "11" +/// avatar : "11" + +class Author { + Author({ + String name, + String avatar,}){ + _name = name; + _avatar = avatar; +} + + Author.fromJson(dynamic json) { + _name = json['name']; + _avatar = json['avatar']; + } + String _name; + String _avatar; + + String get name => _name; + String get avatar => _avatar; + + + set name(String value) { + _name = value; + } + + Map toJson() { + final map = {}; + map['name'] = _name; + map['avatar'] = _avatar; + return map; + } + + set avatar(String value) { + _avatar = value; + } +} \ No newline at end of file diff --git a/lib/retrofit/retrofit_api.dart b/lib/retrofit/retrofit_api.dart index 8df47f39..6ad80563 100644 --- a/lib/retrofit/retrofit_api.dart +++ b/lib/retrofit/retrofit_api.dart @@ -20,6 +20,8 @@ import 'package:retrofit/retrofit.dart'; import 'data/address.dart'; import 'data/banner.dart'; import 'data/brand_data.dart'; +import 'data/category_select_list.dart'; +import 'data/course_list.dart'; import 'data/exchange_order.dart'; import 'data/follow_list.dart'; import 'data/goods.dart'; @@ -40,11 +42,11 @@ import 'data/wx_pay.dart'; part 'retrofit_api.g.dart'; -const base_url = "https://pos.platform.lotus-wallet.com/app/"; ///正式 -const baseUrl = "https://pos.platform.lotus-wallet.com/app/"; ///正式 +// const base_url = "https://pos.platform.lotus-wallet.com/app/"; ///正式 +// const baseUrl = "https://pos.platform.lotus-wallet.com/app/"; ///正式 -// const base_url = "http://192.168.10.236:8766/app/"; ///费韬 -// const baseUrl = "http://192.168.10.236:8766/app/"; ///费韬 +const base_url = "http://192.168.10.236:8766/app/"; ///费韬 +const baseUrl = "http://192.168.10.236:8766/app/"; ///费韬 // const base_url = "http://192.168.10.37:8766/app/"; // const baseUrl = "http://192.168.10.37:8766/app/"; @@ -389,4 +391,16 @@ abstract class ApiService { @GET("/member/socialInfo") Future> socialInfo(); + ///课程分类列表 + @GET("/course/categoryList") + Future>> categoryList(); + + ///课程章节列表 + @GET("/course/catalogList/{courseId}") + Future catalogList(@Path("courseId") String courseId); + + /// 课程列表 + @POST("/course/list") + Future>> courseList(@Body() Map map); + } diff --git a/lib/retrofit/retrofit_api.g.dart b/lib/retrofit/retrofit_api.g.dart index 3c96e516..63ddb880 100644 --- a/lib/retrofit/retrofit_api.g.dart +++ b/lib/retrofit/retrofit_api.g.dart @@ -9,7 +9,7 @@ part of 'retrofit_api.dart'; class _ApiService implements ApiService { _ApiService(this._dio, {this.baseUrl}) { ArgumentError.checkNotNull(_dio, '_dio'); - baseUrl ??= 'https://pos.platform.lotus-wallet.com/app/'; + baseUrl ??= 'http://192.168.10.236:8766/app/'; } final Dio _dio; @@ -1345,4 +1345,75 @@ class _ApiService implements ApiService { ); return value; } + + @override + Future>> categoryList() async { + const _extra = {}; + final queryParameters = {}; + final _data = {}; + final _result = await _dio.request>( + '/course/categoryList', + queryParameters: queryParameters, + options: RequestOptions( + method: 'GET', + headers: {}, + extra: _extra, + baseUrl: baseUrl), + data: _data); + final value = BaseData>.fromJson( + _result.data, + (json) => (json as List) + .map((i) => CategorySelectList.fromJson(i as Map)) + .toList()); + return value; + } + + @override + Future> catalogList(courseId) async { + ArgumentError.checkNotNull(courseId, 'courseId'); + const _extra = {}; + final queryParameters = {}; + final _data = {}; + final _result = await _dio.request>( + '/course/catalogList/$courseId', + queryParameters: queryParameters, + options: RequestOptions( + method: 'GET', + headers: {}, + extra: _extra, + baseUrl: baseUrl), + data: _data); + final value = BaseData.fromJson( + _result.data, + (json) => json as dynamic, + ); + return value; + } + + @override + Future>> courseList(map) async { + ArgumentError.checkNotNull(map, 'map'); + const _extra = {}; + final queryParameters = {}; + final _data = {}; + _data.addAll(map ?? {}); + final _result = await _dio.request>( + '/course/list', + queryParameters: queryParameters, + options: RequestOptions( + method: 'POST', + headers: {}, + extra: _extra, + baseUrl: baseUrl), + data: _data); + final value = BaseData>.fromJson( + _result.data, + (json) => PageInfo.fromJson( + json, + (json) => CourseList.fromJson(json), + ), + ); + return value; + } + }