Merge branch 'design'

This commit is contained in:
guozhigq
2024-10-13 17:42:57 +08:00
13 changed files with 649 additions and 713 deletions

View File

@ -217,12 +217,13 @@ class Request {
/* /*
* get请求 * get请求
*/ */
get(url, {data, options, cancelToken, extra}) async { get(url, {data, Options? options, cancelToken, extra}) async {
Response response; Response response;
final Options options = Options(); options ??= Options(); // 如果 options 为 null则初始化一个新的 Options 对象
ResponseType resType = ResponseType.json; ResponseType resType = ResponseType.json;
if (extra != null) { if (extra != null) {
resType = extra!['resType'] ?? ResponseType.json; resType = extra['resType'] ?? ResponseType.json;
if (extra['ua'] != null) { if (extra['ua'] != null) {
options.headers = {'user-agent': headerUa(type: extra['ua'])}; options.headers = {'user-agent': headerUa(type: extra['ua'])};
} }
@ -238,14 +239,11 @@ class Request {
); );
return response; return response;
} on DioException catch (e) { } on DioException catch (e) {
Response errResponse = Response( return Response(
data: { data: {'message': await ApiInterceptor.dioError(e)},
'message': await ApiInterceptor.dioError(e)
}, // 将自定义 Map 数据赋值给 Response 的 data 属性
statusCode: 200, statusCode: 200,
requestOptions: RequestOptions(), requestOptions: RequestOptions(),
); );
return errResponse;
} }
} }

View File

@ -4,6 +4,7 @@ import 'package:hive/hive.dart';
import 'package:html/parser.dart'; import 'package:html/parser.dart';
import 'package:pilipala/models/member/article.dart'; import 'package:pilipala/models/member/article.dart';
import 'package:pilipala/models/member/like.dart'; import 'package:pilipala/models/member/like.dart';
import 'package:pilipala/utils/global_data_cache.dart';
import '../common/constants.dart'; import '../common/constants.dart';
import '../models/dynamics/result.dart'; import '../models/dynamics/result.dart';
import '../models/follow/result.dart'; import '../models/follow/result.dart';
@ -19,14 +20,20 @@ import 'index.dart';
class MemberHttp { class MemberHttp {
static Future memberInfo({ static Future memberInfo({
int? mid, required int mid,
String token = '', String token = '',
}) async { }) async {
String? wWebid;
if ((await getWWebid(mid: mid))['status']) {
wWebid = GlobalDataCache().wWebid;
}
Map params = await WbiSign().makSign({ Map params = await WbiSign().makSign({
'mid': mid, 'mid': mid,
'token': token, 'token': token,
'platform': 'web', 'platform': 'web',
'web_location': 1550101, 'web_location': 1550101,
...wWebid != null ? {'w_webid': wWebid} : {},
}); });
var res = await Request().get( var res = await Request().get(
Api.memberInfo, Api.memberInfo,
@ -566,6 +573,10 @@ class MemberHttp {
} }
static Future getWWebid({required int mid}) async { static Future getWWebid({required int mid}) async {
String? wWebid = GlobalDataCache().wWebid;
if (wWebid != null) {
return {'status': true, 'data': wWebid};
}
var res = await Request().get('https://space.bilibili.com/$mid/article'); var res = await Request().get('https://space.bilibili.com/$mid/article');
String? headContent = parse(res.data).head?.outerHtml; String? headContent = parse(res.data).head?.outerHtml;
final regex = RegExp( final regex = RegExp(
@ -576,6 +587,7 @@ class MemberHttp {
final content = match.group(1); final content = match.group(1);
String decodedString = Uri.decodeComponent(content!); String decodedString = Uri.decodeComponent(content!);
Map<String, dynamic> map = jsonDecode(decodedString); Map<String, dynamic> map = jsonDecode(decodedString);
GlobalDataCache().wWebid = map['access_id'];
return {'status': true, 'data': map['access_id']}; return {'status': true, 'data': map['access_id']};
} else { } else {
return {'status': false, 'data': '请检查登录状态'}; return {'status': false, 'data': '请检查登录状态'};
@ -588,25 +600,20 @@ class MemberHttp {
static Future getMemberArticle({ static Future getMemberArticle({
required int mid, required int mid,
required int pn, required int pn,
required String wWebid,
String? offset, String? offset,
}) async { }) async {
String? wWebid;
if ((await getWWebid(mid: mid))['status']) {
wWebid = GlobalDataCache().wWebid;
}
Map params = await WbiSign().makSign({ Map params = await WbiSign().makSign({
'host_mid': mid, 'host_mid': mid,
'page': pn, 'page': pn,
'offset': offset, 'offset': offset,
'web_location': 333.999, 'web_location': 333.999,
'w_webid': wWebid, ...wWebid != null ? {'w_webid': wWebid} : {},
});
var res = await Request().get(Api.opusList, data: {
'host_mid': mid,
'page': pn,
'offset': offset,
'web_location': 333.999,
'w_webid': wWebid,
'w_rid': params['w_rid'],
'wts': params['wts'],
}); });
var res = await Request().get(Api.opusList, data: params);
if (res.data['code'] == 0) { if (res.data['code'] == 0) {
return { return {
'status': true, 'status': true,

View File

@ -4,9 +4,12 @@ enum FullScreenGestureMode {
/// 从下滑到上 /// 从下滑到上
fromBottomtoTop, fromBottomtoTop,
/// 关闭手势
none,
} }
extension FullScreenGestureModeExtension on FullScreenGestureMode { extension FullScreenGestureModeExtension on FullScreenGestureMode {
String get values => ['fromToptoBottom', 'fromBottomtoTop'][index]; String get values => ['fromToptoBottom', 'fromBottomtoTop', 'none'][index];
String get labels => ['从上往下滑进入全屏', '从下往上滑进入全屏'][index]; String get labels => ['从上往下滑进入全屏', '从下往上滑进入全屏', '关闭手势'][index];
} }

View File

@ -295,7 +295,7 @@ class AboutController extends GetxController {
displayTime: const Duration(milliseconds: 500), displayTime: const Duration(milliseconds: 500),
).then( ).then(
(value) => launchUrl( (value) => launchUrl(
Uri.parse('https://www.123pan.com/s/9sVqVv-flu0A.html'), Uri.parse('https://www.123684.com/s/9sVqVv-DEZ0A'),
mode: LaunchMode.externalApplication, mode: LaunchMode.externalApplication,
), ),
); );
@ -349,7 +349,7 @@ class AboutController extends GetxController {
// 官网 // 官网
webSiteUrl() { webSiteUrl() {
launchUrl( launchUrl(
Uri.parse('https://pilipalanet.mysxl.cn'), Uri.parse('https://pilipala.life'),
mode: LaunchMode.externalApplication, mode: LaunchMode.externalApplication,
); );
} }

View File

@ -49,6 +49,8 @@ class MemberController extends GetxController {
if (res['status']) { if (res['status']) {
memberInfo.value = res['data']; memberInfo.value = res['data'];
face.value = res['data'].face; face.value = res['data'].face;
} else {
SmartDialog.showToast('用户信息请求异常:${res['msg']}');
} }
return res; return res;
} }
@ -78,42 +80,10 @@ class MemberController extends GetxController {
return; return;
} }
if (attribute.value == 128) { if (attribute.value == 128) {
blockUser(); modifyRelation('block');
return; } else {
modifyRelation('follow');
} }
SmartDialog.show(
useSystem: true,
animationType: SmartAnimationType.centerFade_otherSlide,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('提示'),
content: Text(memberInfo.value.isFollowed! ? '取消关注UP主?' : '关注UP主?'),
actions: [
TextButton(
onPressed: () => SmartDialog.dismiss(),
child: Text(
'点错了',
style: TextStyle(color: Theme.of(context).colorScheme.outline),
),
),
TextButton(
onPressed: () async {
await VideoHttp.relationMod(
mid: mid,
act: memberInfo.value.isFollowed! ? 2 : 1,
reSrc: 11,
);
memberInfo.value.isFollowed = !memberInfo.value.isFollowed!;
relationSearch();
SmartDialog.dismiss();
memberInfo.update((val) {});
},
child: const Text('确认'),
)
],
);
},
);
} }
// 关系查询 // 关系查询
@ -123,24 +93,15 @@ class MemberController extends GetxController {
var res = await UserHttp.hasFollow(mid); var res = await UserHttp.hasFollow(mid);
if (res['status']) { if (res['status']) {
attribute.value = res['data']['attribute']; attribute.value = res['data']['attribute'];
switch (attribute.value) { final Map<int, String> attributeTextMap = {
case 1: 1: '悄悄关注',
attributeText.value = '悄悄关注'; 2: '关注',
break; 6: '已互关',
case 2: 128: '已拉黑',
attributeText.value = '已关注'; };
break; attributeText.value = attributeTextMap[attribute.value] ?? '关注';
case 6:
attributeText.value = '已互关';
break;
case 128:
attributeText.value = '已拉黑';
break;
default:
attributeText.value = '关注';
}
if (res['data']['special'] == 1) { if (res['data']['special'] == 1) {
attributeText.value += 'SP'; attributeText.value = '特别关注';
} }
} }
} }
@ -151,16 +112,37 @@ class MemberController extends GetxController {
SmartDialog.showToast('账号未登录'); SmartDialog.showToast('账号未登录');
return; return;
} }
SmartDialog.show( modifyRelation('block');
useSystem: true, }
animationType: SmartAnimationType.centerFade_otherSlide,
// 合并关注/取关和拉黑逻辑
Future modifyRelation(String actionType) async {
if (userInfo == null) {
SmartDialog.showToast('账号未登录');
return;
}
String contentText;
int act;
if (actionType == 'follow') {
contentText = memberInfo.value.isFollowed! ? '确定取消关注UP主?' : '确定关注UP主?';
act = memberInfo.value.isFollowed! ? 2 : 1;
} else if (actionType == 'block') {
contentText = attribute.value != 128 ? '确定拉黑UP主?' : '确定从黑名单移除UP主';
act = attribute.value != 128 ? 5 : 6;
} else {
return;
}
showDialog(
context: Get.context!,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(
title: const Text('提示'), title: const Text('提示'),
content: Text(attribute.value != 128 ? '确定拉黑UP主?' : '从黑名单移除UP主'), content: Text(contentText),
actions: [ actions: [
TextButton( TextButton(
onPressed: () => SmartDialog.dismiss(), onPressed: () => Navigator.of(context).pop(),
child: Text( child: Text(
'点错了', '点错了',
style: TextStyle(color: Theme.of(context).colorScheme.outline), style: TextStyle(color: Theme.of(context).colorScheme.outline),
@ -170,19 +152,26 @@ class MemberController extends GetxController {
onPressed: () async { onPressed: () async {
var res = await VideoHttp.relationMod( var res = await VideoHttp.relationMod(
mid: mid, mid: mid,
act: attribute.value != 128 ? 5 : 6, act: act,
reSrc: 11, reSrc: 11,
); );
SmartDialog.dismiss(); SmartDialog.dismiss();
if (res['status']) { if (res['status']) {
if (actionType == 'follow') {
memberInfo.value.isFollowed = !memberInfo.value.isFollowed!;
} else if (actionType == 'block') {
attribute.value = attribute.value != 128 ? 128 : 0; attribute.value = attribute.value != 128 ? 128 : 0;
attributeText.value = attribute.value == 128 ? '已拉黑' : '关注'; attributeText.value = attribute.value == 128 ? '已拉黑' : '关注';
memberInfo.value.isFollowed = false; memberInfo.value.isFollowed = false;
}
relationSearch(); relationSearch();
if (context.mounted) {
Navigator.of(context).pop();
}
memberInfo.update((val) {}); memberInfo.update((val) {});
} }
}, },
child: const Text(''), child: const Text(''),
) )
], ],
); );
@ -228,17 +217,14 @@ class MemberController extends GetxController {
// 跳转查看动态 // 跳转查看动态
void pushDynamicsPage() => Get.toNamed('/memberDynamics?mid=$mid'); void pushDynamicsPage() => Get.toNamed('/memberDynamics?mid=$mid');
// 跳转查看投稿 // 跳转查看投稿
void pushArchivesPage() => Get.toNamed('/memberArchive?mid=$mid'); void pushArchivesPage() => Get.toNamed('/memberArchive?mid=$mid');
// 跳转查看专栏
void pushSeasonsPage() {}
// 跳转查看最近投币 // 跳转查看最近投币
void pushRecentCoinsPage() async { void pushRecentCoinsPage() async {
if (recentCoinsList.isNotEmpty) {} if (recentCoinsList.isNotEmpty) {}
} }
// 跳转查看收藏夹
void pushfavPage() => Get.toNamed('/fav?mid=$mid'); void pushfavPage() => Get.toNamed('/fav?mid=$mid');
// 跳转图文专栏 // 跳转图文专栏
void pushArticlePage() => Get.toNamed('/memberArticle?mid=$mid'); void pushArticlePage() => Get.toNamed('/memberArticle?mid=$mid');

View File

@ -1,13 +1,13 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/models/member/info.dart';
import 'package:pilipala/pages/member/index.dart'; import 'package:pilipala/pages/member/index.dart';
import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/utils.dart';
import 'widgets/commen_widget.dart';
import 'widgets/conis.dart'; import 'widgets/conis.dart';
import 'widgets/like.dart'; import 'widgets/like.dart';
import 'widgets/profile.dart'; import 'widgets/profile.dart';
@ -65,10 +65,7 @@ class _MemberPageState extends State<MemberPage>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
primary: true, appBar: AppBar(
body: Column(
children: [
AppBar(
title: StreamBuilder( title: StreamBuilder(
stream: appbarStream.stream.distinct(), stream: appbarStream.stream.distinct(),
initialData: false, initialData: false,
@ -78,8 +75,6 @@ class _MemberPageState extends State<MemberPage>
curve: Curves.easeOut, curve: Curves.easeOut,
duration: const Duration(milliseconds: 500), duration: const Duration(milliseconds: 500),
child: Row( child: Row(
children: [
Row(
children: [ children: [
Obx( Obx(
() => NetworkImgLayer( () => NetworkImgLayer(
@ -94,14 +89,11 @@ class _MemberPageState extends State<MemberPage>
() => Text( () => Text(
_memberController.memberInfo.value.name ?? '', _memberController.memberInfo.value.name ?? '',
style: TextStyle( style: TextStyle(
color: color: Theme.of(context).colorScheme.onSurface,
Theme.of(context).colorScheme.onSurface,
fontSize: 14), fontSize: 14),
), ),
), ),
], ],
)
],
), ),
); );
}, },
@ -148,14 +140,12 @@ class _MemberPageState extends State<MemberPage>
const SizedBox(width: 4), const SizedBox(width: 4),
], ],
), ),
Expanded( primary: true,
child: SingleChildScrollView( body: ListView(
controller: _extendNestCtr, controller: _extendNestCtr,
child: Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom + 20, bottom: MediaQuery.of(context).padding.bottom + 20,
), ),
child: Column(
children: [ children: [
profileWidget(), profileWidget(),
@ -163,10 +153,8 @@ class _MemberPageState extends State<MemberPage>
Obx( Obx(
() => ListTile( () => ListTile(
onTap: _memberController.pushDynamicsPage, onTap: _memberController.pushDynamicsPage,
title: Text( title: Text('${_memberController.isOwner.value ? '' : 'Ta'}的动态'),
'${_memberController.isOwner.value ? '' : 'Ta'}的动态'), trailing: const Icon(Icons.arrow_forward_outlined, size: 19),
trailing:
const Icon(Icons.arrow_forward_outlined, size: 19),
), ),
), ),
@ -174,10 +162,8 @@ class _MemberPageState extends State<MemberPage>
Obx( Obx(
() => ListTile( () => ListTile(
onTap: _memberController.pushArchivesPage, onTap: _memberController.pushArchivesPage,
title: Text( title: Text('${_memberController.isOwner.value ? '' : 'Ta'}的投稿'),
'${_memberController.isOwner.value ? '' : 'Ta'}的投稿'), trailing: const Icon(Icons.arrow_forward_outlined, size: 19),
trailing:
const Icon(Icons.arrow_forward_outlined, size: 19),
), ),
), ),
@ -185,10 +171,8 @@ class _MemberPageState extends State<MemberPage>
Obx( Obx(
() => ListTile( () => ListTile(
onTap: _memberController.pushfavPage, onTap: _memberController.pushfavPage,
title: Text( title: Text('${_memberController.isOwner.value ? '' : 'Ta'}的收藏'),
'${_memberController.isOwner.value ? '' : 'Ta'}的收藏'), trailing: const Icon(Icons.arrow_forward_outlined, size: 19),
trailing:
const Icon(Icons.arrow_forward_outlined, size: 19),
), ),
), ),
@ -196,18 +180,16 @@ class _MemberPageState extends State<MemberPage>
Obx( Obx(
() => ListTile( () => ListTile(
onTap: _memberController.pushArticlePage, onTap: _memberController.pushArticlePage,
title: Text( title: Text('${_memberController.isOwner.value ? '' : 'Ta'}的专栏'),
'${_memberController.isOwner.value ? '' : 'Ta'}的专栏'), trailing: const Icon(Icons.arrow_forward_outlined, size: 19),
trailing:
const Icon(Icons.arrow_forward_outlined, size: 19),
), ),
), ),
/// 合集 /// 合集
Obx( Obx(
() => ListTile( () => ListTile(
title: Text( title: Text('${_memberController.isOwner.value ? '' : 'Ta'}的合集'),
'${_memberController.isOwner.value ? '' : 'Ta'}的合集')), ),
), ),
MediaQuery.removePadding( MediaQuery.removePadding(
removeTop: true, removeTop: true,
@ -216,15 +198,14 @@ class _MemberPageState extends State<MemberPage>
child: FutureBuilder( child: FutureBuilder(
future: _memberSeasonsFuture, future: _memberSeasonsFuture,
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.connectionState == if (snapshot.connectionState == ConnectionState.done) {
ConnectionState.done) {
if (snapshot.data == null) { if (snapshot.data == null) {
return const SizedBox(); return const SizedBox();
} }
if (snapshot.data['status']) { if (snapshot.data['status']) {
Map data = snapshot.data as Map; Map data = snapshot.data as Map;
if (data['data'].seasonsList.isEmpty) { if (data['data'].seasonsList.isEmpty) {
return commenWidget('用户没有设置合集'); return const CommenWidget(msg: '用户没有设置合集');
} else { } else {
return MemberSeasonsPanel(data: data['data']); return MemberSeasonsPanel(data: data['data']);
} }
@ -258,8 +239,7 @@ class _MemberPageState extends State<MemberPage>
child: FutureBuilder( child: FutureBuilder(
future: _memberCoinsFuture, future: _memberCoinsFuture,
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.connectionState == if (snapshot.connectionState == ConnectionState.done) {
ConnectionState.done) {
if (snapshot.data == null) { if (snapshot.data == null) {
return const SizedBox(); return const SizedBox();
} }
@ -296,8 +276,7 @@ class _MemberPageState extends State<MemberPage>
child: FutureBuilder( child: FutureBuilder(
future: _memberLikeFuture, future: _memberLikeFuture,
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.connectionState == if (snapshot.connectionState == ConnectionState.done) {
ConnectionState.done) {
if (snapshot.data == null) { if (snapshot.data == null) {
return const SizedBox(); return const SizedBox();
} }
@ -317,11 +296,6 @@ class _MemberPageState extends State<MemberPage>
), ),
], ],
), ),
),
),
),
],
),
); );
} }
@ -334,11 +308,9 @@ class _MemberPageState extends State<MemberPage>
if (snapshot.connectionState == ConnectionState.done) { if (snapshot.connectionState == ConnectionState.done) {
Map? data = snapshot.data; Map? data = snapshot.data;
if (data != null && data['status']) { if (data != null && data['status']) {
Rx<MemberInfoModel> memberInfo = _memberController.memberInfo;
return Obx( return Obx(
() => Stack( () => Column(
alignment: AlignmentDirectional.center,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
ProfilePanel(ctr: _memberController), ProfilePanel(ctr: _memberController),
@ -347,7 +319,7 @@ class _MemberPageState extends State<MemberPage>
children: [ children: [
Flexible( Flexible(
child: Text( child: Text(
_memberController.memberInfo.value.name!, memberInfo.value.name!,
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: Theme.of(context) style: Theme.of(context)
@ -355,21 +327,20 @@ class _MemberPageState extends State<MemberPage>
.titleMedium! .titleMedium!
.copyWith( .copyWith(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: _memberController.memberInfo.value color: memberInfo.value.vip!.nicknameColor !=
.vip!.nicknameColor !=
null null
? Color(_memberController.memberInfo ? Color(_memberController
.value.vip!.nicknameColor!) .memberInfo.value.vip!.nicknameColor!)
: null), : null),
)), )),
const SizedBox(width: 2), const SizedBox(width: 2),
if (_memberController.memberInfo.value.sex == '') if (memberInfo.value.sex == '')
const Icon( const Icon(
FontAwesomeIcons.venus, FontAwesomeIcons.venus,
size: 14, size: 14,
color: Colors.pink, color: Colors.pink,
), ),
if (_memberController.memberInfo.value.sex == '') if (memberInfo.value.sex == '')
const Icon( const Icon(
FontAwesomeIcons.mars, FontAwesomeIcons.mars,
size: 14, size: 14,
@ -377,72 +348,50 @@ class _MemberPageState extends State<MemberPage>
), ),
const SizedBox(width: 4), const SizedBox(width: 4),
Image.asset( Image.asset(
'assets/images/lv/lv${_memberController.memberInfo.value.level}.png', 'assets/images/lv/lv${memberInfo.value.level}.png',
height: 11, height: 11,
), ),
const SizedBox(width: 6), const SizedBox(width: 6),
if (_memberController if (memberInfo.value.vip!.status == 1 &&
.memberInfo.value.vip!.status == memberInfo
1 && .value.vip!.label!['img_label_uri_hans'] !=
_memberController.memberInfo.value.vip!
.label!['img_label_uri_hans'] !=
'') ...[ '') ...[
Image.network( Image.network(
_memberController.memberInfo.value.vip! memberInfo.value.vip!.label!['img_label_uri_hans'],
.label!['img_label_uri_hans'],
height: 20, height: 20,
), ),
] else if (_memberController ] else if (memberInfo.value.vip!.status == 1 &&
.memberInfo.value.vip!.status == memberInfo.value.vip!
1 &&
_memberController.memberInfo.value.vip!
.label!['img_label_uri_hans_static'] != .label!['img_label_uri_hans_static'] !=
'') ...[ '') ...[
Image.network( Image.network(
_memberController.memberInfo.value.vip! memberInfo
.label!['img_label_uri_hans_static'], .value.vip!.label!['img_label_uri_hans_static'],
height: 20, height: 20,
), ),
] ]
], ],
), ),
if (_memberController if (memberInfo.value.official!['title'] != '') ...[
.memberInfo.value.official!['title'] !=
'') ...[
const SizedBox(height: 6), const SizedBox(height: 6),
Text.rich( Text(
memberInfo.value.official!['role'] == 1
? '个人认证:${memberInfo.value.official!['title']}'
: '企业认证:${memberInfo.value.official!['title']}',
maxLines: 2, maxLines: 2,
TextSpan( softWrap: true,
text: _memberController
.memberInfo.value.official!['role'] ==
1
? '个人认证:'
: '企业认证:',
style: TextStyle( style: TextStyle(
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
), ),
children: [
TextSpan(
text: _memberController
.memberInfo.value.official!['title'],
),
],
),
softWrap: true,
), ),
], ],
const SizedBox(height: 6), const SizedBox(height: 6),
if (_memberController.memberInfo.value.sign != '') SelectableText(memberInfo.value.sign ?? ''),
SelectableText(
_memberController.memberInfo.value.sign!,
),
],
),
], ],
), ),
); );
} else { } else {
return const SizedBox(); return ProfilePanel(ctr: _memberController, loadingStatus: true);
} }
} else { } else {
// 骨架屏 // 骨架屏
@ -452,22 +401,4 @@ class _MemberPageState extends State<MemberPage>
), ),
); );
} }
Widget commenWidget(msg) {
return Padding(
padding: const EdgeInsets.only(
top: 20,
bottom: 30,
),
child: Center(
child: Text(
msg,
style: Theme.of(context)
.textTheme
.labelMedium!
.copyWith(color: Theme.of(context).colorScheme.outline),
),
),
);
}
} }

View File

@ -0,0 +1,24 @@
import 'package:flutter/material.dart';
class CommenWidget extends StatelessWidget {
final String msg;
const CommenWidget({required this.msg, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 10.0),
child: Center(
child: Text(
msg,
textAlign: TextAlign.center,
style: Theme.of(context)
.textTheme
.labelMedium!
.copyWith(color: Theme.of(context).colorScheme.outline),
),
),
);
}
}

View File

@ -18,11 +18,53 @@ class ProfilePanel extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
MemberInfoModel memberInfo = ctr.memberInfo.value; MemberInfoModel memberInfo = ctr.memberInfo.value;
return Builder( final int? mid = memberInfo.mid;
builder: ((context) { final String? name = memberInfo.name;
Map<String, dynamic> buildStatItem({
required String label,
required String value,
required VoidCallback onTap,
}) {
return {
'label': label,
'value': value,
'fn': onTap,
};
}
final List<Map<String, dynamic>> statList = [
buildStatItem(
label: '关注',
value: !loadingStatus ? "${ctr.userStat!['following']}" : '-',
onTap: () {
Get.toNamed('/follow?mid=$mid&name=$name');
},
),
buildStatItem(
label: '粉丝',
value: !loadingStatus
? ctr.userStat!['follower'] != null
? Utils.numFormat(ctr.userStat!['follower'])
: '-'
: '-',
onTap: () {
Get.toNamed('/fan?mid=$mid&name=$name');
},
),
buildStatItem(
label: '获赞',
value: !loadingStatus
? ctr.userStat!['likes'] != null
? Utils.numFormat(ctr.userStat!['likes'])
: '-'
: '-',
onTap: () {},
),
];
return Padding( return Padding(
padding: padding: const EdgeInsets.only(top: 30, left: 4),
EdgeInsets.only(top: MediaQuery.of(context).padding.top - 20),
child: Row( child: Row(
children: [ children: [
Hero( Hero(
@ -43,13 +85,13 @@ class ProfilePanel extends StatelessWidget {
left: 14, left: 14,
child: GestureDetector( child: GestureDetector(
onTap: () { onTap: () {
LiveItemModel liveItem = LiveItemModel.fromJson({ LiveItemModel liveItem = LiveItemModel(
'title': memberInfo.liveRoom!.title, title: memberInfo.liveRoom!.title,
'uname': memberInfo.name, uname: memberInfo.name,
'face': memberInfo.face, face: memberInfo.face,
'roomid': memberInfo.liveRoom!.roomId, roomId: memberInfo.liveRoom!.roomId,
'watched_show': memberInfo.liveRoom!.watchedShow, watchedShow: memberInfo.liveRoom!.watchedShow,
}); );
Get.toNamed( Get.toNamed(
'/liveRoom?roomid=${memberInfo.liveRoom!.roomId}', '/liveRoom?roomid=${memberInfo.liveRoom!.roomId}',
arguments: {'liveItem': liveItem}, arguments: {'liveItem': liveItem},
@ -89,117 +131,80 @@ class ProfilePanel extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Padding( Padding(
padding: padding: const EdgeInsets.symmetric(horizontal: 10),
const EdgeInsets.only(top: 10, left: 10, right: 10),
child: Row( child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: statList.map((item) {
InkWell( return buildStatColumn(
onTap: () { context,
Get.toNamed( item['label'],
'/follow?mid=${memberInfo.mid}&name=${memberInfo.name}'); item['value'],
}, item['fn'],
);
}).toList(),
),
),
const SizedBox(height: 16),
if (ctr.ownerMid != ctr.mid && ctr.ownerMid != -1)
buildActionButtons(context, ctr, memberInfo),
if (ctr.ownerMid == ctr.mid && ctr.ownerMid != -1)
buildEditProfileButton(context),
if (ctr.ownerMid == -1) buildNotLoggedInButton(context),
],
),
),
],
),
);
}
Widget buildStatColumn(
BuildContext context,
String label,
String value,
VoidCallback? onTap,
) {
return InkWell(
onTap: onTap,
child: Column( child: Column(
children: [ children: [
Text( Text(
!loadingStatus value,
? ctr.userStat!['following'].toString() style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
: '-',
style: const TextStyle(
fontWeight: FontWeight.bold),
), ),
Text( Text(
'关注', label,
style: TextStyle( style: TextStyle(
fontSize: Theme.of(context) fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
.textTheme ),
.labelMedium! ),
.fontSize),
)
], ],
), ),
), );
InkWell( }
onTap: () {
Get.toNamed( Widget buildActionButtons(
'/fan?mid=${memberInfo.mid}&name=${memberInfo.name}'); BuildContext context,
}, dynamic ctr,
child: Column( MemberInfoModel memberInfo,
children: [ ) {
Text( ColorScheme colorScheme = Theme.of(context).colorScheme;
!loadingStatus return Row(
? ctr.userStat!['follower'] != null
? Utils.numFormat(
ctr.userStat!['follower'],
)
: '-'
: '-',
style: const TextStyle(
fontWeight: FontWeight.bold)),
Text(
'粉丝',
style: TextStyle(
fontSize: Theme.of(context)
.textTheme
.labelMedium!
.fontSize),
)
],
),
),
Column(
children: [
Text(
!loadingStatus
? ctr.userStat!['likes'] != null
? Utils.numFormat(
ctr.userStat!['likes'],
)
: '-'
: '-',
style: const TextStyle(
fontWeight: FontWeight.bold)),
Text(
'获赞',
style: TextStyle(
fontSize: Theme.of(context)
.textTheme
.labelMedium!
.fontSize),
)
],
),
],
),
),
const SizedBox(height: 10),
if (ctr.ownerMid != ctr.mid && ctr.ownerMid != -1) ...[
Row(
children: [ children: [
const SizedBox(width: 20),
Obx( Obx(
() => Expanded( () => Expanded(
child: TextButton( child: TextButton(
onPressed: () => loadingStatus onPressed: () => loadingStatus ? null : ctr.actionRelationMod(),
? null
: ctr.actionRelationMod(),
style: TextButton.styleFrom( style: TextButton.styleFrom(
foregroundColor: ctr.attribute.value == -1 foregroundColor: ctr.attribute.value == -1
? Colors.transparent ? Colors.transparent
: ctr.attribute.value != 0 : ctr.attribute.value != 0
? Theme.of(context) ? colorScheme.outline
.colorScheme : colorScheme.onPrimary,
.outline
: Theme.of(context)
.colorScheme
.onPrimary,
backgroundColor: ctr.attribute.value != 0 backgroundColor: ctr.attribute.value != 0
? Theme.of(context) ? colorScheme.onInverseSurface
.colorScheme : colorScheme.primary,
.onInverseSurface
: Theme.of(context)
.colorScheme
.primary, // 设置按钮背景色
), ),
child: Obx(() => Text(ctr.attributeText.value)), child: Obx(() => Text(ctr.attributeText.value)),
), ),
@ -220,51 +225,38 @@ class ProfilePanel extends StatelessWidget {
); );
}, },
style: TextButton.styleFrom( style: TextButton.styleFrom(
backgroundColor: Theme.of(context) backgroundColor: colorScheme.onInverseSurface,
.colorScheme
.onInverseSurface,
), ),
child: const Text('发消息'), child: const Text('发消息'),
), ),
) ),
], ],
) );
], }
if (ctr.ownerMid == ctr.mid && ctr.ownerMid != -1) ...[
TextButton( Widget buildEditProfileButton(BuildContext context) {
return TextButton(
onPressed: () { onPressed: () {
SmartDialog.showToast('功能开发中 💪'); SmartDialog.showToast('功能开发中 💪');
}, },
style: TextButton.styleFrom( style: TextButton.styleFrom(
padding: const EdgeInsets.only(left: 80, right: 80), padding: const EdgeInsets.symmetric(horizontal: 80),
foregroundColor: foregroundColor: Theme.of(context).colorScheme.onPrimary,
Theme.of(context).colorScheme.onPrimary, backgroundColor: Theme.of(context).colorScheme.primary,
backgroundColor:
Theme.of(context).colorScheme.primary,
), ),
child: const Text('编辑资料'), child: const Text('编辑资料'),
) );
], }
if (ctr.ownerMid == -1) ...[
TextButton( Widget buildNotLoggedInButton(BuildContext context) {
return TextButton(
onPressed: () {}, onPressed: () {},
style: TextButton.styleFrom( style: TextButton.styleFrom(
padding: const EdgeInsets.only(left: 80, right: 80), padding: const EdgeInsets.symmetric(horizontal: 80),
foregroundColor: foregroundColor: Theme.of(context).colorScheme.outline,
Theme.of(context).colorScheme.outline, backgroundColor: Theme.of(context).colorScheme.onInverseSurface,
backgroundColor:
Theme.of(context).colorScheme.onInverseSurface,
), ),
child: const Text('未登录'), child: const Text('未登录'),
)
]
],
),
),
],
),
);
}),
); );
} }
} }

View File

@ -10,7 +10,6 @@ class MemberArticleController extends GetxController {
int pn = 1; int pn = 1;
String? offset; String? offset;
bool hasMore = true; bool hasMore = true;
String? wWebid;
RxBool isLoading = false.obs; RxBool isLoading = false.obs;
RxList<MemberArticleItemModel> articleList = <MemberArticleItemModel>[].obs; RxList<MemberArticleItemModel> articleList = <MemberArticleItemModel>[].obs;
@ -20,25 +19,11 @@ class MemberArticleController extends GetxController {
mid = int.parse(Get.parameters['mid']!); mid = int.parse(Get.parameters['mid']!);
} }
// 获取wWebid
Future getWWebid() async {
var res = await MemberHttp.getWWebid(mid: mid);
if (res['status']) {
wWebid = res['data'];
} else {
wWebid = '-1';
SmartDialog.showToast(res['msg']);
}
}
Future getMemberArticle(type) async { Future getMemberArticle(type) async {
if (isLoading.value) { if (isLoading.value) {
return; return;
} }
isLoading.value = true; isLoading.value = true;
if (wWebid == null) {
await getWWebid();
}
if (type == 'init') { if (type == 'init') {
pn = 1; pn = 1;
articleList.clear(); articleList.clear();
@ -47,7 +32,6 @@ class MemberArticleController extends GetxController {
mid: mid, mid: mid,
pn: pn, pn: pn,
offset: offset, offset: offset,
wWebid: wWebid!,
); );
if (res['status']) { if (res['status']) {
offset = res['data'].offset; offset = res['data'].offset;

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:pilipala/utils/global_data_cache.dart'; import 'package:pilipala/utils/global_data_cache.dart';
@ -22,7 +23,7 @@ class _PlayGesturePageState extends State<PlayGesturePage> {
void initState() { void initState() {
super.initState(); super.initState();
fullScreenGestureMode = setting.get(SettingBoxKey.fullScreenGestureMode, fullScreenGestureMode = setting.get(SettingBoxKey.fullScreenGestureMode,
defaultValue: FullScreenGestureMode.values.last.index); defaultValue: FullScreenGestureMode.fromBottomtoTop.index);
} }
@override @override
@ -71,6 +72,7 @@ class _PlayGesturePageState extends State<PlayGesturePage> {
GlobalDataCache().fullScreenGestureMode.index; GlobalDataCache().fullScreenGestureMode.index;
setting.put( setting.put(
SettingBoxKey.fullScreenGestureMode, fullScreenGestureMode); SettingBoxKey.fullScreenGestureMode, fullScreenGestureMode);
SmartDialog.showToast('设置成功');
setState(() {}); setState(() {});
} }
}, },

View File

@ -204,6 +204,14 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
widget.controller.brightness.value = value; widget.controller.brightness.value = value;
} }
bool isUsingFullScreenGestures(double tapPosition, double sectionWidth) {
if (fullScreenGestureMode == FullScreenGestureMode.none) {
return false;
} else {
return tapPosition < sectionWidth * 2;
}
}
@override @override
void dispose() { void dispose() {
animationController.dispose(); animationController.dispose();
@ -660,12 +668,12 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
_brightnessValue.value - delta / level; _brightnessValue.value - delta / level;
final double result = brightness.clamp(0.0, 1.0); final double result = brightness.clamp(0.0, 1.0);
setBrightness(result); setBrightness(result);
} else if (tapPosition < sectionWidth * 2) { } else if (isUsingFullScreenGestures(tapPosition, sectionWidth)) {
// 全屏 // 全屏
final double dy = details.delta.dy; final double dy = details.delta.dy;
const double threshold = 7.0; // 滑动阈值 const double threshold = 7.0; // 滑动阈值
final bool flag = final bool flag = fullScreenGestureMode !=
fullScreenGestureMode != FullScreenGestureMode.values.last; FullScreenGestureMode.fromBottomtoTop;
if (dy > _distance.value && if (dy > _distance.value &&
dy > threshold && dy > threshold &&
!_.controlsLock.value) { !_.controlsLock.value) {

View File

@ -15,6 +15,7 @@ class GlobalDataCache {
late FullScreenGestureMode fullScreenGestureMode; late FullScreenGestureMode fullScreenGestureMode;
late bool enablePlayerControlAnimation; late bool enablePlayerControlAnimation;
late List<String> actionTypeSort; late List<String> actionTypeSort;
String? wWebid;
/// 播放器相关 /// 播放器相关
// 弹幕开关 // 弹幕开关

View File

@ -306,7 +306,7 @@ class Utils {
onPressed: () async { onPressed: () async {
await SmartDialog.dismiss(); await SmartDialog.dismiss();
launchUrl( launchUrl(
Uri.parse('https://www.123pan.com/s/9sVqVv-flu0A.html'), Uri.parse('https://www.123684.com/s/9sVqVv-DEZ0A'),
mode: LaunchMode.externalApplication, mode: LaunchMode.externalApplication,
); );
}, },