mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
feat: show total season/series
Closes #164 Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -328,6 +328,8 @@ class Api {
|
|||||||
|
|
||||||
static const String spaceFav = '/x/v3/fav/folder/space';
|
static const String spaceFav = '/x/v3/fav/folder/space';
|
||||||
|
|
||||||
|
static const String seasonSeries = '/x/polymer/web-space/seasons_series_list';
|
||||||
|
|
||||||
// 用户名片信息
|
// 用户名片信息
|
||||||
static const String memberCardInfo = '/x/web-interface/card';
|
static const String memberCardInfo = '/x/web-interface/card';
|
||||||
|
|
||||||
|
|||||||
@@ -154,6 +154,25 @@ class MemberHttp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<LoadingState> seasonSeriesList({
|
||||||
|
required int? mid,
|
||||||
|
required int pn,
|
||||||
|
}) async {
|
||||||
|
dynamic res = await Request().get(
|
||||||
|
Api.seasonSeries,
|
||||||
|
queryParameters: {
|
||||||
|
'mid': mid,
|
||||||
|
'page_num': pn,
|
||||||
|
'page_size': 10,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return LoadingState.success(res.data['data']['items_lists']);
|
||||||
|
} else {
|
||||||
|
return LoadingState.error(res.data['message']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static Future<LoadingState> spaceArchive({
|
static Future<LoadingState> spaceArchive({
|
||||||
required ContributeType type,
|
required ContributeType type,
|
||||||
required int? mid,
|
required int? mid,
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ class Data {
|
|||||||
dynamic digitalButton;
|
dynamic digitalButton;
|
||||||
dynamic entry;
|
dynamic entry;
|
||||||
dynamic live;
|
dynamic live;
|
||||||
|
UgcSeason? ugcSeason;
|
||||||
|
|
||||||
Data({
|
Data({
|
||||||
this.relation,
|
this.relation,
|
||||||
@@ -79,9 +80,22 @@ class Data {
|
|||||||
this.digitalButton,
|
this.digitalButton,
|
||||||
this.entry,
|
this.entry,
|
||||||
this.live,
|
this.live,
|
||||||
|
this.ugcSeason,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Data.fromJson(Map<String, dynamic> json) => _$DataFromJson(json);
|
factory Data.fromJson(Map<String, dynamic> json) => _$DataFromJson(json);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$DataToJson(this);
|
Map<String, dynamic> toJson() => _$DataToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class UgcSeason {
|
||||||
|
int? count;
|
||||||
|
|
||||||
|
UgcSeason({
|
||||||
|
this.count,
|
||||||
|
});
|
||||||
|
|
||||||
|
UgcSeason.fromJson(Map<String, dynamic> json) {
|
||||||
|
count = json['count'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -62,6 +62,9 @@ Data _$DataFromJson(Map<String, dynamic> json) => Data(
|
|||||||
digitalButton: json['digital_button'],
|
digitalButton: json['digital_button'],
|
||||||
entry: json['entry'],
|
entry: json['entry'],
|
||||||
live: json['live'],
|
live: json['live'],
|
||||||
|
ugcSeason: json['ugc_season'] != null
|
||||||
|
? UgcSeason.fromJson(json['ugc_season'])
|
||||||
|
: null,
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$DataToJson(Data instance) => <String, dynamic>{
|
Map<String, dynamic> _$DataToJson(Data instance) => <String, dynamic>{
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
|
import 'package:PiliPlus/http/member.dart';
|
||||||
|
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||||
|
|
||||||
|
class SeasonSeriesController extends CommonController {
|
||||||
|
SeasonSeriesController(this.mid);
|
||||||
|
final int mid;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
queryData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool customHandleResponse(Success response) {
|
||||||
|
Map data = response.response;
|
||||||
|
List list = ((data['seasons_list'] as List?) ?? []) +
|
||||||
|
((data['series_list'] as List?) ?? []);
|
||||||
|
if (currentPage != 0 && loadingState.value is Success) {
|
||||||
|
list.insertAll(0, (loadingState.value as Success).response);
|
||||||
|
}
|
||||||
|
isEnd = list.length >= ((data['page']['total'] as int?) ?? 0);
|
||||||
|
loadingState.value = LoadingState.success(list);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<LoadingState> customGetData() => MemberHttp.seasonSeriesList(
|
||||||
|
mid: mid,
|
||||||
|
pn: currentPage,
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
import 'package:PiliPlus/common/constants.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
||||||
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
|
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/season_series/controller.dart';
|
||||||
|
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/season_series/widget/season_series_card.dart';
|
||||||
|
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/video/member_video.dart';
|
||||||
|
import 'package:PiliPlus/pages/member/new/content/member_contribute/member_contribute.dart';
|
||||||
|
import 'package:PiliPlus/utils/grid.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
class SeasonSeriesPage extends StatefulWidget {
|
||||||
|
const SeasonSeriesPage({
|
||||||
|
super.key,
|
||||||
|
required this.mid,
|
||||||
|
this.heroTag,
|
||||||
|
});
|
||||||
|
|
||||||
|
final int mid;
|
||||||
|
final String? heroTag;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SeasonSeriesPage> createState() => _SeasonSeriesPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SeasonSeriesPageState extends State<SeasonSeriesPage> {
|
||||||
|
late final _controller = Get.put(
|
||||||
|
SeasonSeriesController(widget.mid),
|
||||||
|
tag: widget.heroTag,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Obx(() => _buildBody(_controller.loadingState.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildBody(LoadingState loadingState) {
|
||||||
|
return switch (loadingState) {
|
||||||
|
Loading() => loadingWidget,
|
||||||
|
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||||
|
? CustomScrollView(
|
||||||
|
slivers: [
|
||||||
|
SliverPadding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
top: StyleString.safeSpace - 5,
|
||||||
|
bottom: MediaQuery.paddingOf(context).bottom + 80,
|
||||||
|
),
|
||||||
|
sliver: SliverGrid(
|
||||||
|
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||||
|
mainAxisSpacing: 2,
|
||||||
|
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
|
||||||
|
childAspectRatio: StyleString.aspectRatio * 2.2,
|
||||||
|
),
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(context, index) {
|
||||||
|
if (index == loadingState.response.length - 1) {
|
||||||
|
_controller.onLoadMore();
|
||||||
|
}
|
||||||
|
return SeasonSeriesCard(
|
||||||
|
item: loadingState.response[index],
|
||||||
|
onTap: () {
|
||||||
|
dynamic item = loadingState.response[index];
|
||||||
|
bool isSeason = item['meta']['season_id'] != null;
|
||||||
|
dynamic id = isSeason
|
||||||
|
? item['meta']['season_id']
|
||||||
|
: item['meta']['series_id'];
|
||||||
|
Get.to(
|
||||||
|
Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(item['meta']['name']),
|
||||||
|
),
|
||||||
|
body: MemberVideo(
|
||||||
|
type: isSeason
|
||||||
|
? ContributeType.season
|
||||||
|
: ContributeType.series,
|
||||||
|
heroTag: widget.heroTag,
|
||||||
|
mid: widget.mid,
|
||||||
|
seasonId: isSeason ? id : null,
|
||||||
|
seriesId: isSeason ? null : id,
|
||||||
|
title: item['meta']['name'],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
childCount: loadingState.response.length,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: scrollErrorWidget(
|
||||||
|
callback: () {
|
||||||
|
_controller.onReload();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Error() => scrollErrorWidget(
|
||||||
|
errMsg: loadingState.errMsg,
|
||||||
|
callback: () {
|
||||||
|
_controller.onReload();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
LoadingState() => throw UnimplementedError(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
import 'package:PiliPlus/common/constants.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/image_save.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
|
||||||
|
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/video/member_video.dart';
|
||||||
|
import 'package:PiliPlus/pages/member/new/content/member_contribute/member_contribute.dart';
|
||||||
|
import 'package:PiliPlus/utils/utils.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
class SeasonSeriesCard extends StatelessWidget {
|
||||||
|
const SeasonSeriesCard({
|
||||||
|
super.key,
|
||||||
|
required this.item,
|
||||||
|
required this.onTap,
|
||||||
|
});
|
||||||
|
final dynamic item;
|
||||||
|
final VoidCallback onTap;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return InkWell(
|
||||||
|
onLongPress: () {
|
||||||
|
imageSaveDialog(
|
||||||
|
context: context,
|
||||||
|
title: item['meta']['name'],
|
||||||
|
cover: item['meta']['cover'],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onTap: onTap,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: StyleString.safeSpace,
|
||||||
|
vertical: 5,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
AspectRatio(
|
||||||
|
aspectRatio: StyleString.aspectRatio,
|
||||||
|
child: LayoutBuilder(
|
||||||
|
builder: (BuildContext context, BoxConstraints boxConstraints) {
|
||||||
|
final double maxWidth = boxConstraints.maxWidth;
|
||||||
|
final double maxHeight = boxConstraints.maxHeight;
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
NetworkImgLayer(
|
||||||
|
src: item['meta']['cover'],
|
||||||
|
width: maxWidth,
|
||||||
|
height: maxHeight,
|
||||||
|
),
|
||||||
|
PBadge(
|
||||||
|
text:
|
||||||
|
'${item['meta']['season_id'] != null ? '合集' : '列表'}: ${item['meta']['total']}',
|
||||||
|
bottom: 6.0,
|
||||||
|
right: 6.0,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
videoContent(context)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget videoContent(context) {
|
||||||
|
return Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(10, 0, 6, 0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
item['meta']['name'],
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
|
||||||
|
height: 1.42,
|
||||||
|
letterSpacing: 0.3,
|
||||||
|
),
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
Text(
|
||||||
|
Utils.dateFormat(item['meta']['ptime']),
|
||||||
|
maxLines: 1,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||||
|
height: 1,
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
overflow: TextOverflow.clip,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/article/member_article.dart';
|
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/article/member_article.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/audio/member_audio.dart';
|
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/audio/member_audio.dart';
|
||||||
|
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/season_series/season_series_page.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/video/member_video.dart';
|
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/video/member_video.dart';
|
||||||
import 'package:PiliPlus/pages/member/new/content/member_contribute/member_contribute_ctr.dart';
|
import 'package:PiliPlus/pages/member/new/content/member_contribute/member_contribute_ctr.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -110,6 +111,10 @@ class _MemberContributeState extends State<MemberContribute>
|
|||||||
seriesId: item.seriesId,
|
seriesId: item.seriesId,
|
||||||
title: item.title,
|
title: item.title,
|
||||||
),
|
),
|
||||||
|
'ugcSeason' => SeasonSeriesPage(
|
||||||
|
mid: widget.mid,
|
||||||
|
heroTag: widget.heroTag,
|
||||||
|
),
|
||||||
_ => Center(child: Text(item.title!))
|
_ => Center(child: Text(item.title!))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -32,6 +32,18 @@ class MemberContributeCtr extends CommonController
|
|||||||
if (contribute.items?.isNullOrEmpty == false &&
|
if (contribute.items?.isNullOrEmpty == false &&
|
||||||
contribute.items!.length > 1) {
|
contribute.items!.length > 1) {
|
||||||
items = contribute.items;
|
items = contribute.items;
|
||||||
|
if (_ctr.ugcSeasonCount != null) {
|
||||||
|
int currentSeasonCount =
|
||||||
|
items!.where((item) => item.param == 'season_video').length;
|
||||||
|
if (currentSeasonCount < _ctr.ugcSeasonCount!) {
|
||||||
|
items!.add(
|
||||||
|
Item(
|
||||||
|
param: 'ugcSeason',
|
||||||
|
title: '全部合集/列表',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
tabs = items!.map((item) => Tab(text: item.title)).toList();
|
tabs = items!.map((item) => Tab(text: item.title)).toList();
|
||||||
tabController = TabController(
|
tabController = TabController(
|
||||||
vsync: this,
|
vsync: this,
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ class MemberControllerNew extends CommonController
|
|||||||
List<Tab2>? tab2;
|
List<Tab2>? tab2;
|
||||||
RxInt contributeInitialIndex = 0.obs;
|
RxInt contributeInitialIndex = 0.obs;
|
||||||
double? top;
|
double? top;
|
||||||
|
int? ugcSeasonCount;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
@@ -54,6 +55,7 @@ class MemberControllerNew extends CommonController
|
|||||||
tab2 = response.response.tab2;
|
tab2 = response.response.tab2;
|
||||||
live = response.response?.live;
|
live = response.response?.live;
|
||||||
silence = response.response?.card?.silence;
|
silence = response.response?.card?.silence;
|
||||||
|
ugcSeasonCount = response.response?.ugcSeason?.count;
|
||||||
if (response.response?.card?.endTime != null) {
|
if (response.response?.card?.endTime != null) {
|
||||||
if (response.response.card.endTime == 0) {
|
if (response.response.card.endTime == 0) {
|
||||||
endTime = ': 永久封禁';
|
endTime = ': 永久封禁';
|
||||||
|
|||||||
Reference in New Issue
Block a user