mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
feat: show video tags
This commit is contained in:
@@ -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';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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!);
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用户主页
|
// 用户主页
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user