feat: show video tags

This commit is contained in:
bggRGjQaUbCoE
2024-09-27 18:34:22 +08:00
parent b88cfae8d9
commit f3744c23bc
10 changed files with 100 additions and 9 deletions

View File

@@ -609,4 +609,6 @@ class Api {
/// 取消订阅-播单 /// 取消订阅-播单
static const String unfavFolder = '/x/v3/fav/folder/unfav'; static const String unfavFolder = '/x/v3/fav/folder/unfav';
static const String videoTags = '/x/tag/archive/tags';
} }

View File

@@ -392,4 +392,13 @@ class UserHttp {
return {'status': false, 'msg': res.data['message']}; 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};
}
}
} }

View File

@@ -1,4 +1,5 @@
import 'package:PiliPalaX/http/loading_state.dart'; import 'package:PiliPalaX/http/loading_state.dart';
import 'package:PiliPalaX/http/user.dart';
import 'package:PiliPalaX/pages/common/common_controller.dart'; import 'package:PiliPalaX/pages/common/common_controller.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@@ -45,6 +46,7 @@ class BangumiIntroController extends CommonController {
RxBool hasCoin = false.obs; RxBool hasCoin = false.obs;
// 是否收藏 // 是否收藏
RxBool hasFav = false.obs; RxBool hasFav = false.obs;
dynamic videoTags;
Box userInfoCache = GStorage.userInfo; Box userInfoCache = GStorage.userInfo;
bool userLogin = false; bool userLogin = false;
Rx<FavFolderData> favFolderData = FavFolderData().obs; Rx<FavFolderData> favFolderData = FavFolderData().obs;
@@ -58,6 +60,7 @@ class BangumiIntroController extends CommonController {
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
queryVideoTags();
if (Get.arguments.isNotEmpty as bool) { if (Get.arguments.isNotEmpty as bool) {
if (Get.arguments.containsKey('bangumiItem') as bool) { if (Get.arguments.containsKey('bangumiItem') as bool) {
preRender = true; preRender = true;
@@ -94,6 +97,14 @@ class BangumiIntroController extends CommonController {
queryData(); queryData();
} }
Future queryVideoTags() async {
var result = await UserHttp.videoTags(bvid: bvid);
if (result['status']) {
videoTags = result['data'];
debugPrint('tags: ${result['data']}');
}
}
@override @override
bool customHandleResponse(Success response) { bool customHandleResponse(Success response) {
epId = response.response.episodes!.first.id; epId = response.response.episodes!.first.id;

View File

@@ -77,7 +77,10 @@ class _BangumiIntroPanelState extends State<BangumiIntroPanel>
bangumiDetail: loadingState.response, bangumiDetail: loadingState.response,
cid: cid, cid: cid,
showEpisodes: widget.showEpisodes, showEpisodes: widget.showEpisodes,
showIntroDetail: widget.showIntroDetail, showIntroDetail: () => widget.showIntroDetail(
loadingState.response,
bangumiIntroController.videoTags,
),
) )
: loadingState is Error : loadingState is Error
? HttpError( ? HttpError(
@@ -165,7 +168,7 @@ class _BangumiInfoState extends State<BangumiInfo> {
// 视频介绍 // 视频介绍
showIntroDetail() { showIntroDetail() {
feedBack(); feedBack();
widget.showIntroDetail(widget.bangumiDetail); widget.showIntroDetail();
} }
@override @override

View File

@@ -1,16 +1,19 @@
import 'package:PiliPalaX/pages/search/widgets/search_text.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:PiliPalaX/common/widgets/stat/danmu.dart'; import 'package:PiliPalaX/common/widgets/stat/danmu.dart';
import 'package:PiliPalaX/common/widgets/stat/view.dart'; import 'package:PiliPalaX/common/widgets/stat/view.dart';
import 'package:get/get.dart';
import '../../../../utils/utils.dart'; import '../../../../utils/utils.dart';
class IntroDetail extends StatelessWidget { class IntroDetail extends StatelessWidget {
final dynamic bangumiDetail; final dynamic bangumiDetail;
final dynamic videoTags;
const IntroDetail({ const IntroDetail({
Key? key, Key? key,
this.bangumiDetail, this.bangumiDetail,
this.videoTags,
}) : super(key: key); }) : super(key: key);
@override @override
@@ -108,6 +111,23 @@ class IntroDetail extends StatelessWidget {
bangumiDetail.actors, bangumiDetail.actors,
style: smallTitle.copyWith(fontSize: 13), 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) SizedBox(height: MediaQuery.of(context).padding.bottom + 20)
], ],
), ),

View File

@@ -5,12 +5,14 @@ class SearchText extends StatelessWidget {
final Function? onSelect; final Function? onSelect;
final int? searchTextIdx; final int? searchTextIdx;
final Function? onLongSelect; final Function? onLongSelect;
final double? fontSize;
const SearchText({ const SearchText({
super.key, super.key,
this.searchText, this.searchText,
this.onSelect, this.onSelect,
this.searchTextIdx, this.searchTextIdx,
this.onLongSelect, this.onLongSelect,
this.fontSize,
}); });
@override @override
@@ -34,6 +36,7 @@ class SearchText extends StatelessWidget {
child: Text( child: Text(
searchText!, searchText!,
style: TextStyle( style: TextStyle(
fontSize: fontSize,
color: Theme.of(context).colorScheme.onSurfaceVariant), color: Theme.of(context).colorScheme.onSurfaceVariant),
), ),
), ),

View File

@@ -47,6 +47,8 @@ class VideoIntroController extends GetxController {
Rx<Map<String, dynamic>> userStat = Rx<Map<String, dynamic>> userStat =
Rx<Map<String, dynamic>>({'follower': '-'}); Rx<Map<String, dynamic>>({'follower': '-'});
dynamic videoTags;
// 是否点赞 // 是否点赞
RxBool hasLike = false.obs; RxBool hasLike = false.obs;
// 是否点踩 // 是否点踩
@@ -120,6 +122,7 @@ class VideoIntroController extends GetxController {
// 获取视频简介&分p // 获取视频简介&分p
void queryVideoIntro() async { void queryVideoIntro() async {
queryVideoTags();
var result = await VideoHttp.videoIntro(bvid: bvid); var result = await VideoHttp.videoIntro(bvid: bvid);
if (result['status']) { if (result['status']) {
videoDetail.value = result['data']!; 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主粉丝数 // 获取up主粉丝数
Future queryUserStat() async { Future queryUserStat() async {
var result = await UserHttp.userStat(mid: videoDetail.value.owner!.mid!); var result = await UserHttp.userStat(mid: videoDetail.value.owner!.mid!);

View File

@@ -82,7 +82,10 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
videoDetail: videoDetail, videoDetail: videoDetail,
heroTag: heroTag, heroTag: heroTag,
showAiBottomSheet: widget.showAiBottomSheet, showAiBottomSheet: widget.showAiBottomSheet,
showIntroDetail: widget.showIntroDetail, showIntroDetail: () => widget.showIntroDetail(
videoDetail,
videoIntroController.videoTags,
),
showEpisodes: widget.showEpisodes, showEpisodes: widget.showEpisodes,
) )
: VideoInfo( : VideoInfo(
@@ -92,7 +95,10 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
videoDetail: videoIntroController.videoDetail.value, videoDetail: videoIntroController.videoDetail.value,
heroTag: heroTag, heroTag: heroTag,
showAiBottomSheet: widget.showAiBottomSheet, showAiBottomSheet: widget.showAiBottomSheet,
showIntroDetail: widget.showIntroDetail, showIntroDetail: () => widget.showIntroDetail(
videoIntroController.videoDetail.value,
videoIntroController.videoTags,
),
showEpisodes: widget.showEpisodes, showEpisodes: widget.showEpisodes,
)); ));
} }
@@ -199,7 +205,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
return; return;
} }
feedBack(); feedBack();
widget.showIntroDetail(widget.videoDetail); widget.showIntroDetail();
} }
// 用户主页 // 用户主页

View File

@@ -1,3 +1,4 @@
import 'package:PiliPalaX/pages/search/widgets/search_text.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
@@ -10,8 +11,10 @@ class IntroDetail extends StatelessWidget {
const IntroDetail({ const IntroDetail({
super.key, super.key,
this.videoDetail, this.videoDetail,
this.videoTags,
}); });
final dynamic videoDetail; final dynamic videoDetail;
final dynamic videoTags;
@override @override
Widget build(BuildContext context) { 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), const SizedBox(height: 10),
SizedBox( SizedBox(
width: double.infinity, width: double.infinity,

View File

@@ -1296,12 +1296,18 @@ class _VideoDetailPageState extends State<VideoDetailPage>
); );
} }
showIntroDetail(videoDetail) { showIntroDetail(videoDetail, videoTags) {
scaffoldKey.currentState?.showBottomSheet( scaffoldKey.currentState?.showBottomSheet(
enableDrag: true, enableDrag: true,
(context) => videoDetail is BangumiInfoModel (context) => videoDetail is BangumiInfoModel
? bangumi.IntroDetail(bangumiDetail: videoDetail) ? bangumi.IntroDetail(
: video.IntroDetail(videoDetail: videoDetail), bangumiDetail: videoDetail,
videoTags: videoTags,
)
: video.IntroDetail(
videoDetail: videoDetail,
videoTags: videoTags,
),
); );
} }