From f3744c23bcdb93dc3cb533a4868b9e352f7ed8e1 Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Fri, 27 Sep 2024 18:34:22 +0800 Subject: [PATCH] feat: show video tags --- lib/http/api.dart | 2 ++ lib/http/user.dart | 9 ++++++++ .../bangumi/introduction/controller.dart | 11 ++++++++++ lib/pages/bangumi/introduction/view.dart | 7 ++++-- .../introduction/widgets/intro_detail.dart | 22 ++++++++++++++++++- lib/pages/search/widgets/search_text.dart | 3 +++ .../video/detail/introduction/controller.dart | 11 ++++++++++ lib/pages/video/detail/introduction/view.dart | 12 +++++++--- .../introduction/widgets/intro_detail.dart | 20 +++++++++++++++++ lib/pages/video/detail/view.dart | 12 +++++++--- 10 files changed, 100 insertions(+), 9 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index 71b5e386..61131545 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -609,4 +609,6 @@ class Api { /// 取消订阅-播单 static const String unfavFolder = '/x/v3/fav/folder/unfav'; + + static const String videoTags = '/x/tag/archive/tags'; } diff --git a/lib/http/user.dart b/lib/http/user.dart index 02c8a232..c7e8867c 100644 --- a/lib/http/user.dart +++ b/lib/http/user.dart @@ -392,4 +392,13 @@ class UserHttp { return {'status': false, 'msg': res.data['message']}; } } + + static videoTags({required String bvid}) async { + var res = await Request().get(Api.videoTags, data: {'bvid': bvid}); + if (res.data['code'] == 0) { + return {'status': true, 'data': res.data['data']}; + } else { + return {'status': false}; + } + } } diff --git a/lib/pages/bangumi/introduction/controller.dart b/lib/pages/bangumi/introduction/controller.dart index 30cf7a02..56e5ebcb 100644 --- a/lib/pages/bangumi/introduction/controller.dart +++ b/lib/pages/bangumi/introduction/controller.dart @@ -1,4 +1,5 @@ import 'package:PiliPalaX/http/loading_state.dart'; +import 'package:PiliPalaX/http/user.dart'; import 'package:PiliPalaX/pages/common/common_controller.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -45,6 +46,7 @@ class BangumiIntroController extends CommonController { RxBool hasCoin = false.obs; // 是否收藏 RxBool hasFav = false.obs; + dynamic videoTags; Box userInfoCache = GStorage.userInfo; bool userLogin = false; Rx favFolderData = FavFolderData().obs; @@ -58,6 +60,7 @@ class BangumiIntroController extends CommonController { @override void onInit() { super.onInit(); + queryVideoTags(); if (Get.arguments.isNotEmpty as bool) { if (Get.arguments.containsKey('bangumiItem') as bool) { preRender = true; @@ -94,6 +97,14 @@ class BangumiIntroController extends CommonController { queryData(); } + Future queryVideoTags() async { + var result = await UserHttp.videoTags(bvid: bvid); + if (result['status']) { + videoTags = result['data']; + debugPrint('tags: ${result['data']}'); + } + } + @override bool customHandleResponse(Success response) { epId = response.response.episodes!.first.id; diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart index ae112ba6..f8d30dce 100644 --- a/lib/pages/bangumi/introduction/view.dart +++ b/lib/pages/bangumi/introduction/view.dart @@ -77,7 +77,10 @@ class _BangumiIntroPanelState extends State bangumiDetail: loadingState.response, cid: cid, showEpisodes: widget.showEpisodes, - showIntroDetail: widget.showIntroDetail, + showIntroDetail: () => widget.showIntroDetail( + loadingState.response, + bangumiIntroController.videoTags, + ), ) : loadingState is Error ? HttpError( @@ -165,7 +168,7 @@ class _BangumiInfoState extends State { // 视频介绍 showIntroDetail() { feedBack(); - widget.showIntroDetail(widget.bangumiDetail); + widget.showIntroDetail(); } @override diff --git a/lib/pages/bangumi/introduction/widgets/intro_detail.dart b/lib/pages/bangumi/introduction/widgets/intro_detail.dart index af6fbabc..04a6a2b7 100644 --- a/lib/pages/bangumi/introduction/widgets/intro_detail.dart +++ b/lib/pages/bangumi/introduction/widgets/intro_detail.dart @@ -1,16 +1,19 @@ +import 'package:PiliPalaX/pages/search/widgets/search_text.dart'; import 'package:flutter/material.dart'; import 'package:PiliPalaX/common/widgets/stat/danmu.dart'; import 'package:PiliPalaX/common/widgets/stat/view.dart'; +import 'package:get/get.dart'; import '../../../../utils/utils.dart'; - class IntroDetail extends StatelessWidget { final dynamic bangumiDetail; + final dynamic videoTags; const IntroDetail({ Key? key, this.bangumiDetail, + this.videoTags, }) : super(key: key); @override @@ -108,6 +111,23 @@ class IntroDetail extends StatelessWidget { bangumiDetail.actors, style: smallTitle.copyWith(fontSize: 13), ), + if (videoTags is List && videoTags.isNotEmpty) ...[ + const SizedBox(height: 10), + Wrap( + spacing: 8, + runSpacing: 8, + children: (videoTags as List) + .map( + (item) => SearchText( + fontSize: 13, + searchText: item['tag_name'], + onSelect: (_) => Get.toNamed('/searchResult', + parameters: {'keyword': item['tag_name']}), + ), + ) + .toList(), + ) + ], SizedBox(height: MediaQuery.of(context).padding.bottom + 20) ], ), diff --git a/lib/pages/search/widgets/search_text.dart b/lib/pages/search/widgets/search_text.dart index 039a851b..c8f7cf41 100644 --- a/lib/pages/search/widgets/search_text.dart +++ b/lib/pages/search/widgets/search_text.dart @@ -5,12 +5,14 @@ class SearchText extends StatelessWidget { final Function? onSelect; final int? searchTextIdx; final Function? onLongSelect; + final double? fontSize; const SearchText({ super.key, this.searchText, this.onSelect, this.searchTextIdx, this.onLongSelect, + this.fontSize, }); @override @@ -34,6 +36,7 @@ class SearchText extends StatelessWidget { child: Text( searchText!, style: TextStyle( + fontSize: fontSize, color: Theme.of(context).colorScheme.onSurfaceVariant), ), ), diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 323d788d..3e4dd05e 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -47,6 +47,8 @@ class VideoIntroController extends GetxController { Rx> userStat = Rx>({'follower': '-'}); + dynamic videoTags; + // 是否点赞 RxBool hasLike = false.obs; // 是否点踩 @@ -120,6 +122,7 @@ class VideoIntroController extends GetxController { // 获取视频简介&分p void queryVideoIntro() async { + queryVideoTags(); var result = await VideoHttp.videoIntro(bvid: bvid); if (result['status']) { videoDetail.value = result['data']!; @@ -159,6 +162,14 @@ class VideoIntroController extends GetxController { } } + Future queryVideoTags() async { + var result = await UserHttp.videoTags(bvid: bvid); + if (result['status']) { + videoTags = result['data']; + debugPrint('tags: ${result['data']}'); + } + } + // 获取up主粉丝数 Future queryUserStat() async { var result = await UserHttp.userStat(mid: videoDetail.value.owner!.mid!); diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 245c01ae..1f5c5288 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -82,7 +82,10 @@ class _VideoIntroPanelState extends State videoDetail: videoDetail, heroTag: heroTag, showAiBottomSheet: widget.showAiBottomSheet, - showIntroDetail: widget.showIntroDetail, + showIntroDetail: () => widget.showIntroDetail( + videoDetail, + videoIntroController.videoTags, + ), showEpisodes: widget.showEpisodes, ) : VideoInfo( @@ -92,7 +95,10 @@ class _VideoIntroPanelState extends State videoDetail: videoIntroController.videoDetail.value, heroTag: heroTag, showAiBottomSheet: widget.showAiBottomSheet, - showIntroDetail: widget.showIntroDetail, + showIntroDetail: () => widget.showIntroDetail( + videoIntroController.videoDetail.value, + videoIntroController.videoTags, + ), showEpisodes: widget.showEpisodes, )); } @@ -199,7 +205,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { return; } feedBack(); - widget.showIntroDetail(widget.videoDetail); + widget.showIntroDetail(); } // 用户主页 diff --git a/lib/pages/video/detail/introduction/widgets/intro_detail.dart b/lib/pages/video/detail/introduction/widgets/intro_detail.dart index 671708a1..d4be9a50 100644 --- a/lib/pages/video/detail/introduction/widgets/intro_detail.dart +++ b/lib/pages/video/detail/introduction/widgets/intro_detail.dart @@ -1,3 +1,4 @@ +import 'package:PiliPalaX/pages/search/widgets/search_text.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -10,8 +11,10 @@ class IntroDetail extends StatelessWidget { const IntroDetail({ super.key, this.videoDetail, + this.videoTags, }); final dynamic videoDetail; + final dynamic videoTags; @override Widget build(BuildContext context) { @@ -75,6 +78,23 @@ class IntroDetail extends StatelessWidget { ), ], ), + if (videoTags is List && videoTags.isNotEmpty) ...[ + const SizedBox(height: 10), + Wrap( + spacing: 8, + runSpacing: 8, + children: (videoTags as List) + .map( + (item) => SearchText( + fontSize: 13, + searchText: item['tag_name'], + onSelect: (_) => Get.toNamed('/searchResult', + parameters: {'keyword': item['tag_name']}), + ), + ) + .toList(), + ) + ], const SizedBox(height: 10), SizedBox( width: double.infinity, diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 8e552614..73aab22c 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -1296,12 +1296,18 @@ class _VideoDetailPageState extends State ); } - showIntroDetail(videoDetail) { + showIntroDetail(videoDetail, videoTags) { scaffoldKey.currentState?.showBottomSheet( enableDrag: true, (context) => videoDetail is BangumiInfoModel - ? bangumi.IntroDetail(bangumiDetail: videoDetail) - : video.IntroDetail(videoDetail: videoDetail), + ? bangumi.IntroDetail( + bangumiDetail: videoDetail, + videoTags: videoTags, + ) + : video.IntroDetail( + videoDetail: videoDetail, + videoTags: videoTags, + ), ); }