From dc9b345e99436c7eec3256958277557a896d2440 Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Mon, 9 Jun 2025 12:00:35 +0800 Subject: [PATCH] opt ui Signed-off-by: bggRGjQaUbCoE --- lib/http/api.dart | 2 + lib/http/user.dart | 15 + lib/models/model_avatar.dart | 6 +- lib/pages/dynamics_detail/view.dart | 9 +- lib/pages/member/controller.dart | 10 + lib/pages/member/view.dart | 21 +- lib/pages/mine/view.dart | 358 +++++++++--------- lib/pages/pgc_review/view.dart | 1 + .../introduction/pgc/widgets/pgc_panel.dart | 8 +- 9 files changed, 234 insertions(+), 196 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index 56c64c7f..f42694e3 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -884,4 +884,6 @@ class Api { static const String spaceSetting = '/x/space/setting/app'; static const String spaceSettingMod = '/x/space/privacy/batch/modify'; + + static const String vipExpAdd = '/x/vip/experience/add'; } diff --git a/lib/http/user.dart b/lib/http/user.dart index ff801c25..d270828e 100644 --- a/lib/http/user.dart +++ b/lib/http/user.dart @@ -383,4 +383,19 @@ class UserHttp { return {'status': false, 'msg': res.data['message']}; } } + + static Future vipExpAdd() async { + final res = await Request().post( + Api.vipExpAdd, + queryParameters: { + 'mid': Accounts.main.mid, + 'csrf': Accounts.main.csrf, + }, + ); + if (res.data['code'] == 0) { + return {'status': true}; + } else { + return {'status': false, 'msg': res.data['message']}; + } + } } diff --git a/lib/models/model_avatar.dart b/lib/models/model_avatar.dart index d533d48a..2966fd45 100644 --- a/lib/models/model_avatar.dart +++ b/lib/models/model_avatar.dart @@ -43,9 +43,9 @@ class Vip { Label? label; Vip.fromJson(Map json) { - type = json['type']; - status = json['status'] ?? 0; - dueDate = json['due_date']; + type = json['type'] ?? json['vipType']; + status = json['status'] ?? json['vipStatus'] ?? 0; + dueDate = json['due_date'] ?? json['vipDueDate']; if (json['label'] != null) label = Label.fromJson(json['label']); } } diff --git a/lib/pages/dynamics_detail/view.dart b/lib/pages/dynamics_detail/view.dart index 0ea96d99..0a3fa21a 100644 --- a/lib/pages/dynamics_detail/view.dart +++ b/lib/pages/dynamics_detail/view.dart @@ -251,9 +251,12 @@ class _DynamicDetailPageState extends State return AnimatedOpacity( opacity: _visibleTitle.value ? 1 : 0, duration: const Duration(milliseconds: 300), - child: AuthorPanel( - item: _controller.dynItem, - isDetail: true, + child: IgnorePointer( + ignoring: !_visibleTitle.value, + child: AuthorPanel( + item: _controller.dynItem, + isDetail: true, + ), ), ); }, diff --git a/lib/pages/member/controller.dart b/lib/pages/member/controller.dart index 8262a469..856e665f 100644 --- a/lib/pages/member/controller.dart +++ b/lib/pages/member/controller.dart @@ -2,6 +2,7 @@ import 'dart:math'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/http/member.dart'; +import 'package:PiliPlus/http/user.dart'; import 'package:PiliPlus/http/video.dart'; import 'package:PiliPlus/models/common/member/tab_type.dart'; import 'package:PiliPlus/models_new/space/space/data.dart'; @@ -255,4 +256,13 @@ class MemberController extends CommonDataController ); } } + + Future vipExpAdd() async { + var res = await UserHttp.vipExpAdd(); + if (res['status']) { + SmartDialog.showToast('领取成功'); + } else { + SmartDialog.showToast(res['msg']); + } + } } diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index 4ce348d0..d7206912 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -136,7 +136,22 @@ class _MemberPageState extends State { ), ), if (_userController.ownerMid != 0) - if (_userController.mid == _userController.ownerMid) + if (_userController.mid == _userController.ownerMid) ...[ + if ((_userController + .loadingState.value.data?.card?.vip?.status ?? + 0) > + 0) + PopupMenuItem( + onTap: _userController.vipExpAdd, + child: const Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.upcoming_outlined, size: 19), + SizedBox(width: 10), + Text('大会员经验'), + ], + ), + ), PopupMenuItem( onTap: () => Get.toNamed('/spaceSetting'), child: const Row( @@ -147,8 +162,8 @@ class _MemberPageState extends State { Text('空间设置'), ], ), - ) - else ...[ + ), + ] else ...[ const PopupMenuDivider(), PopupMenuItem( onTap: () => showDialog( diff --git a/lib/pages/mine/view.dart b/lib/pages/mine/view.dart index aaa4650b..9deb19a4 100644 --- a/lib/pages/mine/view.dart +++ b/lib/pages/mine/view.dart @@ -1,8 +1,10 @@ import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; +import 'package:PiliPlus/models/common/image_type.dart'; import 'package:PiliPlus/models/common/theme/theme_type.dart'; import 'package:PiliPlus/models/user/info.dart'; import 'package:PiliPlus/pages/mine/controller.dart'; +import 'package:PiliPlus/utils/extension.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; @@ -18,67 +20,63 @@ class _MinePageState extends State { final MineController _mineController = Get.put(MineController()) ..queryUserInfo(); - Widget _header(ThemeData theme) => FittedBox( - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const SizedBox(width: 12), - Image.asset( - 'assets/images/logo/logo.png', - width: 35, + Widget _header(ThemeData theme) => Row( + children: [ + const SizedBox(width: 12), + Image.asset( + 'assets/images/logo/logo.png', + width: 35, + ), + const SizedBox(width: 5), + Text( + 'PiliPlus', + style: theme.textTheme.titleMedium, + ), + const Spacer(), + IconButton( + iconSize: 40.0, + padding: const EdgeInsets.all(8), + style: const ButtonStyle( + tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - const SizedBox(width: 5), - Text( - 'PiliPlus', - style: theme.textTheme.titleMedium, + tooltip: "${MineController.anonymity.value ? '退出' : '进入'}无痕模式", + onPressed: () { + MineController.onChangeAnonymity(context); + setState(() {}); + }, + icon: MineController.anonymity.value + ? const Icon(MdiIcons.incognito, size: 24) + : const Icon(MdiIcons.incognitoOff, size: 24), + ), + Obx( + () { + return IconButton( + iconSize: 40.0, + padding: const EdgeInsets.all(8), + style: const ButtonStyle( + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + tooltip: '切换至${_mineController.nextThemeType.description}主题', + onPressed: _mineController.onChangeTheme, + icon: _mineController.themeType.value.icon, + ); + }, + ), + IconButton( + iconSize: 40.0, + padding: const EdgeInsets.all(8), + style: const ButtonStyle( + tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - const SizedBox(width: 30), - IconButton( - iconSize: 40.0, - padding: const EdgeInsets.all(8), - style: const ButtonStyle( - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - ), - tooltip: "${MineController.anonymity.value ? '退出' : '进入'}无痕模式", - onPressed: () { - MineController.onChangeAnonymity(context); - setState(() {}); - }, - icon: MineController.anonymity.value - ? const Icon(MdiIcons.incognito, size: 24) - : const Icon(MdiIcons.incognitoOff, size: 24), - ), - Obx( - () { - return IconButton( - iconSize: 40.0, - padding: const EdgeInsets.all(8), - style: const ButtonStyle( - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - ), - tooltip: '切换至${_mineController.nextThemeType.description}主题', - onPressed: _mineController.onChangeTheme, - icon: _mineController.themeType.value.icon, - ); - }, - ), - IconButton( - iconSize: 40.0, - padding: const EdgeInsets.all(8), - style: const ButtonStyle( - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - ), - tooltip: '设置', - onPressed: () => { - Get.back(), - Get.toNamed('/setting', preventDuplicates: false), - }, - icon: const Icon(MdiIcons.cogs, size: 24), - ), - const SizedBox(width: 10), - ], - ), + tooltip: '设置', + onPressed: () => { + Get.back(), + Get.toNamed('/setting', preventDuplicates: false), + }, + icon: const Icon(MdiIcons.cogs, size: 24), + ), + const SizedBox(width: 10), + ], ); @override @@ -98,7 +96,8 @@ class _MinePageState extends State { } Widget userInfoBuild(ThemeData theme) { - LevelInfo? levelInfo = _mineController.userInfo.value.levelInfo; + UserInfoData userInfo = _mineController.userInfo.value; + LevelInfo? levelInfo = userInfo.levelInfo; TextStyle style = TextStyle( fontSize: theme.textTheme.titleMedium!.fontSize, color: theme.colorScheme.primary, @@ -115,28 +114,38 @@ class _MinePageState extends State { mainAxisSize: MainAxisSize.min, children: [ const SizedBox(width: 20), - ClipOval( - child: _mineController.userInfo.value.face != null - ? NetworkImgLayer( - src: _mineController.userInfo.value.face, - semanticsLabel: '头像', + userInfo.face != null + ? Stack( + clipBehavior: Clip.none, + children: [ + NetworkImgLayer( + src: userInfo.face, + semanticsLabel: '头像', + type: ImageType.avatar, + width: 55, + height: 55, + ), + if (userInfo.vipStatus != null && + userInfo.vipStatus! > 0) + Positioned( + right: -1, + bottom: -2, + child: Image.asset( + 'assets/images/big-vip.png', + height: 19, + semanticLabel: "大会员", + ), + ), + ], + ) + : ClipOval( + child: Image.asset( width: 55, height: 55, - ) - : Container( - width: 55, - height: 55, - alignment: Alignment.center, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: theme.colorScheme.onInverseSurface, - ), - child: Image.asset( - 'assets/images/noface.jpeg', - semanticLabel: "默认头像", - ), + 'assets/images/noface.jpeg', + semanticLabel: "默认头像", ), - ), + ), const SizedBox(width: 16), Expanded( child: Column( @@ -144,24 +153,27 @@ class _MinePageState extends State { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ - FittedBox( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - _mineController.userInfo.value.uname ?? '点击头像登录', - style: theme.textTheme.titleMedium! - .copyWith(height: 1), + Row( + spacing: 4, + children: [ + Flexible( + child: Text( + userInfo.uname ?? '点击头像登录', + style: theme.textTheme.titleMedium!.copyWith( + height: 1, + color: userInfo.vipStatus != null && + userInfo.vipStatus! > 0 && + userInfo.vipType == 2 + ? context.vipColor + : null, + ), ), - const SizedBox(width: 4), - Image.asset( - 'assets/images/lv/lv${_mineController.userInfo.value.isSeniorMember == 1 ? '6_s' : _mineController.userInfo.value.levelInfo != null ? _mineController.userInfo.value.levelInfo!.currentLevel : '0'}.png', - height: 10, - semanticLabel: - '等级:${_mineController.userInfo.value.levelInfo != null ? _mineController.userInfo.value.levelInfo!.currentLevel : '0'}', - ), - ], - ), + ), + Image.asset( + 'assets/images/lv/lv${levelInfo == null ? 0 : userInfo.isSeniorMember == 1 ? '6_s' : levelInfo.currentLevel}.png', + height: 10, + ), + ], ), const SizedBox(height: 8), FittedBox( @@ -176,9 +188,7 @@ class _MinePageState extends State { ), ), TextSpan( - text: - (_mineController.userInfo.value.money ?? '-') - .toString(), + text: userInfo.money?.toString() ?? '-', style: TextStyle( fontSize: theme.textTheme.labelSmall!.fontSize, fontWeight: FontWeight.bold, @@ -186,7 +196,7 @@ class _MinePageState extends State { ), ), TextSpan( - text: " 经验 ", + text: " 经验 ", style: TextStyle( fontSize: theme.textTheme.labelSmall!.fontSize, color: theme.colorScheme.outline, @@ -215,17 +225,14 @@ class _MinePageState extends State { ), ), const SizedBox(height: 4), - SizedBox( - height: 2, - child: LinearProgressIndicator( - minHeight: 2, - value: levelInfo != null - ? (levelInfo.currentExp! / levelInfo.nextExp!) - : 0, - backgroundColor: theme.colorScheme.inversePrimary, - valueColor: AlwaysStoppedAnimation( - theme.colorScheme.primary), - ), + LinearProgressIndicator( + minHeight: 2, + value: levelInfo != null + ? (levelInfo.currentExp! / levelInfo.nextExp!) + : 0, + backgroundColor: theme.colorScheme.inversePrimary, + valueColor: AlwaysStoppedAnimation( + theme.colorScheme.primary), ), ], ), @@ -235,83 +242,70 @@ class _MinePageState extends State { ), ), const SizedBox(height: 10), - SizedBox( - width: 240, - height: 100, - child: GridView.count( - padding: EdgeInsets.zero, - physics: const NeverScrollableScrollPhysics(), - crossAxisCount: 3, - children: [ - InkWell( - onTap: _mineController.pushDynamic, - borderRadius: StyleString.mdRadius, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - (_mineController.userStat.value.dynamicCount ?? '-') - .toString(), - key: ValueKey(_mineController - .userStat.value.dynamicCount - .toString()), - style: style, - ), - const SizedBox(height: 8), - Text( - '动态', - style: theme.textTheme.labelMedium, - ), - ], + Center( + child: SizedBox( + width: 240, + child: Row( + children: [ + _btn( + count: _mineController.userStat.value.dynamicCount, + countStyle: style, + name: '动态', + nameStyle: theme.textTheme.labelMedium, + onTap: _mineController.pushDynamic, ), - ), - InkWell( - onTap: _mineController.pushFollow, - borderRadius: StyleString.mdRadius, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - (_mineController.userStat.value.following ?? '-') - .toString(), - key: ValueKey( - _mineController.userStat.value.following.toString()), - style: style, - ), - const SizedBox(height: 8), - Text( - '关注', - style: theme.textTheme.labelMedium, - ), - ], + _btn( + count: _mineController.userStat.value.following, + countStyle: style, + name: '关注', + nameStyle: theme.textTheme.labelMedium, + onTap: _mineController.pushFollow, ), - ), - InkWell( - onTap: _mineController.pushFans, - borderRadius: StyleString.mdRadius, - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - (_mineController.userStat.value.follower ?? '-') - .toString(), - key: ValueKey( - _mineController.userStat.value.follower.toString()), - style: style, - ), - const SizedBox(height: 8), - Text( - '粉丝', - style: theme.textTheme.labelMedium, - ), - ], + _btn( + count: _mineController.userStat.value.follower, + countStyle: style, + name: '粉丝', + nameStyle: theme.textTheme.labelMedium, + onTap: _mineController.pushFans, ), - ), - ], + ], + ), ), ), + const SizedBox(height: 20), ], ); } + + Widget _btn({ + required int? count, + required TextStyle countStyle, + required String name, + required TextStyle? nameStyle, + required VoidCallback onTap, + }) { + return InkWell( + onTap: onTap, + borderRadius: StyleString.mdRadius, + child: SizedBox( + width: 80, + height: 80, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + count?.toString() ?? '-', + style: countStyle, + ), + const SizedBox(height: 8), + Text( + name, + style: nameStyle, + ), + ], + ), + ), + ); + } } diff --git a/lib/pages/pgc_review/view.dart b/lib/pages/pgc_review/view.dart index 15c2aa52..c0608e47 100644 --- a/lib/pages/pgc_review/view.dart +++ b/lib/pages/pgc_review/view.dart @@ -50,6 +50,7 @@ class _PgcReviewPageState extends State indicatorWeight: 0, overlayColor: WidgetStateProperty.all(Colors.transparent), splashFactory: NoSplash.splashFactory, + padding: const EdgeInsets.only(left: 6), indicatorPadding: const EdgeInsets.symmetric(horizontal: 3, vertical: 8), indicator: BoxDecoration( diff --git a/lib/pages/video/introduction/pgc/widgets/pgc_panel.dart b/lib/pages/video/introduction/pgc/widgets/pgc_panel.dart index 5302968d..5f57878d 100644 --- a/lib/pages/video/introduction/pgc/widgets/pgc_panel.dart +++ b/lib/pages/video/introduction/pgc/widgets/pgc_panel.dart @@ -193,14 +193,13 @@ class _PgcPanelState extends State { const SizedBox(width: 2), if (item.badge != null) ...[ const Spacer(), - if (item.badge == '会员') ...[ + if (item.badge == '会员') Image.asset( 'assets/images/big-vip.png', height: 16, semanticLabel: "大会员", - ), - ], - if (item.badge != '会员') ...[ + ) + else Text( item.badge!, style: TextStyle( @@ -208,7 +207,6 @@ class _PgcPanelState extends State { color: theme.colorScheme.primary, ), ), - ], ] ], ),