个人资料卡
This commit is contained in:
parent
23748737a2
commit
e41cb3fb24
@ -13,6 +13,7 @@ import 'package:yumi/modules/admin/editing/sc_editing_user_room_page.dart';
|
||||
import 'package:yumi/modules/admin/search/sc_edit_room_search_admin_page.dart';
|
||||
import 'package:yumi/modules/admin/search/sc_edit_user_search_admin_page.dart';
|
||||
import 'package:yumi/modules/user/profile/person_detail_page.dart';
|
||||
import 'package:yumi/modules/user/profile/profile_gift_wall_detail_page.dart';
|
||||
import 'package:yumi/modules/search/sc_search_page.dart';
|
||||
import 'package:yumi/modules/media/image_preview_page.dart';
|
||||
import 'package:yumi/modules/media/video_player_page.dart';
|
||||
@ -26,6 +27,7 @@ import '../user/edit/edit_user_info_page2.dart';
|
||||
class SCMainRoute implements SCIRouterProvider {
|
||||
static String report = '/main/report';
|
||||
static String person = '/main/person';
|
||||
static String giftWall = '/main/person/giftWall';
|
||||
static String edit = '/main/person/edit';
|
||||
static String follow = '/main/me/follow';
|
||||
static String vistors = '/main/me/vistors';
|
||||
@ -63,6 +65,14 @@ class SCMainRoute implements SCIRouterProvider {
|
||||
),
|
||||
),
|
||||
);
|
||||
router.define(
|
||||
giftWall,
|
||||
handler: Handler(
|
||||
handlerFunc:
|
||||
(_, params) =>
|
||||
ProfileGiftWallDetailPage(tageId: params['tageId']!.first),
|
||||
),
|
||||
);
|
||||
router.define(
|
||||
mainSearch,
|
||||
handler: Handler(handlerFunc: (_, params) => SearchPage()),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
495
lib/modules/user/profile/profile_gift_wall_detail_page.dart
Normal file
495
lib/modules/user/profile/profile_gift_wall_detail_page.dart
Normal file
@ -0,0 +1,495 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
||||
import 'package:yumi/services/auth/user_profile_manager.dart';
|
||||
import 'package:yumi/shared/business_logic/models/res/gift_res.dart';
|
||||
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
|
||||
import 'package:yumi/shared/data_sources/sources/repositories/sc_gift_repository_imp.dart';
|
||||
import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart';
|
||||
import 'package:yumi/services/general/sc_app_general_manager.dart';
|
||||
import 'package:yumi/ui_kit/components/sc_compontent.dart';
|
||||
import 'package:yumi/ui_kit/components/text/sc_text.dart';
|
||||
|
||||
class ProfileGiftWallDetailPage extends StatefulWidget {
|
||||
const ProfileGiftWallDetailPage({super.key, required this.tageId});
|
||||
|
||||
final String tageId;
|
||||
|
||||
@override
|
||||
State<ProfileGiftWallDetailPage> createState() =>
|
||||
_ProfileGiftWallDetailPageState();
|
||||
}
|
||||
|
||||
class _ProfileGiftWallDetailPageState extends State<ProfileGiftWallDetailPage> {
|
||||
static const Color _profileBg = Color(0xff072121);
|
||||
static const Color _cardBg = Color(0xff08251E);
|
||||
static const Color _profileBorder = Color(0xffB2FBCC);
|
||||
|
||||
SocialChatUserProfile? _profile;
|
||||
List<SocialChatGiftRes> _gifts = [];
|
||||
bool _loadingGifts = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_primeProfile();
|
||||
_loadProfile();
|
||||
_loadGiftWall();
|
||||
}
|
||||
|
||||
void _primeProfile() {
|
||||
final currentProfile =
|
||||
Provider.of<SocialChatUserProfileManager>(
|
||||
context,
|
||||
listen: false,
|
||||
).userProfile;
|
||||
if (currentProfile?.id == widget.tageId) {
|
||||
_profile = currentProfile;
|
||||
}
|
||||
}
|
||||
|
||||
void _loadProfile() {
|
||||
SCAccountRepository()
|
||||
.loadUserInfo(widget.tageId)
|
||||
.then((profile) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_profile = profile;
|
||||
});
|
||||
})
|
||||
.catchError((_) {});
|
||||
}
|
||||
|
||||
Future<void> _loadGiftWall() async {
|
||||
final appGeneralManager = Provider.of<SCAppGeneralManager>(
|
||||
context,
|
||||
listen: false,
|
||||
);
|
||||
try {
|
||||
final result = await SCGiftRepositoryImp().giftWall(widget.tageId);
|
||||
final resolvedGifts = await _resolveGiftWallMeta(
|
||||
result,
|
||||
appGeneralManager,
|
||||
);
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_gifts = resolvedGifts;
|
||||
_loadingGifts = false;
|
||||
});
|
||||
} catch (_) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_loadingGifts = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<SocialChatGiftRes>> _resolveGiftWallMeta(
|
||||
List<SocialChatGiftRes> gifts,
|
||||
SCAppGeneralManager appGeneralManager,
|
||||
) async {
|
||||
if (gifts.isEmpty) return gifts;
|
||||
if (appGeneralManager.giftResList.isEmpty) {
|
||||
await appGeneralManager.giftList(includeCustomized: false);
|
||||
}
|
||||
return gifts
|
||||
.map((gift) => _mergeGiftMeta(gift, appGeneralManager))
|
||||
.toList();
|
||||
}
|
||||
|
||||
SocialChatGiftRes _mergeGiftMeta(
|
||||
SocialChatGiftRes gift,
|
||||
SCAppGeneralManager appGeneralManager,
|
||||
) {
|
||||
final meta = _findGiftMeta(gift, appGeneralManager);
|
||||
if (meta == null) return gift;
|
||||
return gift.copyWith(
|
||||
giftName: _firstNonBlank([gift.giftName, meta.giftName]),
|
||||
giftCandy: gift.giftCandy ?? meta.giftCandy,
|
||||
giftIntegral: gift.giftIntegral ?? meta.giftIntegral,
|
||||
giftPhoto: _firstNonBlank([gift.giftPhoto, meta.giftPhoto]),
|
||||
giftSourceUrl: _firstNonBlank([gift.giftSourceUrl, meta.giftSourceUrl]),
|
||||
giftCode: _firstNonBlank([gift.giftCode, meta.giftCode]),
|
||||
standardId: _firstNonBlank([gift.standardId, meta.standardId]),
|
||||
);
|
||||
}
|
||||
|
||||
SocialChatGiftRes? _findGiftMeta(
|
||||
SocialChatGiftRes gift,
|
||||
SCAppGeneralManager appGeneralManager,
|
||||
) {
|
||||
final idCandidates = <String>[
|
||||
gift.standardId ?? "",
|
||||
gift.id ?? "",
|
||||
gift.giftCode ?? "",
|
||||
].where((id) => id.trim().isNotEmpty && id.trim() != "0");
|
||||
|
||||
for (final id in idCandidates) {
|
||||
final meta = appGeneralManager.getGiftByIdOrStandardId(id);
|
||||
if (meta != null) return meta;
|
||||
}
|
||||
|
||||
final giftPhoto = (gift.giftPhoto ?? "").trim();
|
||||
final giftCode = (gift.giftCode ?? "").trim();
|
||||
for (final meta in appGeneralManager.giftResList) {
|
||||
if (giftPhoto.isNotEmpty && giftPhoto == (meta.giftPhoto ?? "").trim()) {
|
||||
return meta;
|
||||
}
|
||||
if (giftCode.isNotEmpty && giftCode == (meta.giftCode ?? "").trim()) {
|
||||
return meta;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String? _firstNonBlank(Iterable<String?> values) {
|
||||
for (final value in values) {
|
||||
final text = value?.trim();
|
||||
if (text != null && text.isNotEmpty) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
int get _receivedCount {
|
||||
return _gifts.fold<int>(0, (sum, gift) => sum + _giftQuantity(gift));
|
||||
}
|
||||
|
||||
num get _totalValue {
|
||||
return _gifts.fold<num>(
|
||||
0,
|
||||
(sum, gift) => sum + (_giftAmount(gift) * _giftQuantity(gift)),
|
||||
);
|
||||
}
|
||||
|
||||
int _giftQuantity(SocialChatGiftRes gift) {
|
||||
return int.tryParse(gift.quantity ?? "") ?? 0;
|
||||
}
|
||||
|
||||
num _giftAmount(SocialChatGiftRes gift) {
|
||||
return gift.giftCandy ?? gift.giftIntegral ?? 0;
|
||||
}
|
||||
|
||||
String _formatNumber(num value) {
|
||||
if (value % 1 == 0) return value.toInt().toString();
|
||||
return value.toStringAsFixed(1);
|
||||
}
|
||||
|
||||
Widget _buildGradientBorder({
|
||||
double? width,
|
||||
double? height,
|
||||
required double radius,
|
||||
required Widget child,
|
||||
Color backgroundColor = _cardBg,
|
||||
double endAlpha = 0.42,
|
||||
}) {
|
||||
final borderRadius = BorderRadius.circular(radius);
|
||||
return Container(
|
||||
width: width,
|
||||
height: height,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: borderRadius,
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [_profileBorder, _profileBorder.withValues(alpha: endAlpha)],
|
||||
),
|
||||
),
|
||||
padding: EdgeInsets.all(1.w),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor,
|
||||
borderRadius: BorderRadius.circular((radius - 1.w).clamp(0, radius)),
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTopBar() {
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
height: 54.w,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
PositionedDirectional(
|
||||
start: 8.w,
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () => SCNavigatorUtils.goBack(context),
|
||||
child: SizedBox(
|
||||
width: 52.w,
|
||||
height: 52.w,
|
||||
child: Icon(
|
||||
Icons.keyboard_arrow_left_rounded,
|
||||
color: Colors.white,
|
||||
size: 38.w,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"Gift Wall",
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 19.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildProfileHeader() {
|
||||
return Column(
|
||||
children: [
|
||||
SizedBox(height: 12.w),
|
||||
head(
|
||||
url: _profile?.userAvatar ?? "",
|
||||
width: 104.w,
|
||||
headdress: _profile?.getHeaddress()?.sourceUrl,
|
||||
),
|
||||
SizedBox(height: 10.w),
|
||||
socialchatNickNameText(
|
||||
_profile?.userNickname ?? "",
|
||||
maxWidth: 300.w,
|
||||
fontSize: 20,
|
||||
textColor: Colors.white,
|
||||
fontWeight: FontWeight.w400,
|
||||
type: _profile?.getVIP()?.name ?? "",
|
||||
needScroll: (_profile?.userNickname?.characters.length ?? 0) > 13,
|
||||
),
|
||||
SizedBox(height: 18.w),
|
||||
_buildSummaryCard(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSummaryCard() {
|
||||
return _buildGradientBorder(
|
||||
width: 355.w,
|
||||
height: 51.w,
|
||||
radius: 8.w,
|
||||
backgroundColor: _cardBg,
|
||||
endAlpha: 0.52,
|
||||
child: Row(
|
||||
children: [
|
||||
_buildSummaryItem(
|
||||
value: _formatNumber(_receivedCount),
|
||||
label: "Received",
|
||||
),
|
||||
_buildSummaryDivider(),
|
||||
_buildSummaryItem(
|
||||
value: _formatNumber(_totalValue),
|
||||
label: "Total Value",
|
||||
showCoin: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSummaryItem({
|
||||
required String value,
|
||||
required String label,
|
||||
bool showCoin = false,
|
||||
}) {
|
||||
return Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (showCoin) ...[_buildCoinIcon(14.w), SizedBox(width: 3.w)],
|
||||
text(
|
||||
value,
|
||||
fontSize: 17,
|
||||
textColor: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
],
|
||||
),
|
||||
text(label, fontSize: 14, textColor: const Color(0xffB1B1B1)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSummaryDivider() {
|
||||
return Container(
|
||||
height: 28.w,
|
||||
width: 1.w,
|
||||
decoration: const BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
Color.fromRGBO(255, 255, 255, 0),
|
||||
Colors.white,
|
||||
Color.fromRGBO(255, 255, 255, 0),
|
||||
],
|
||||
stops: [0, 0.4862, 1],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildGiftGrid() {
|
||||
if (_loadingGifts) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: 80.w),
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
width: 24.w,
|
||||
height: 24.w,
|
||||
child: const CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
color: _profileBorder,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (_gifts.isEmpty) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: 90.w),
|
||||
child: Opacity(
|
||||
opacity: 0.42,
|
||||
child: Image.asset(
|
||||
"sc_images/room/sc_icon_room_music_empty.png",
|
||||
width: 72.w,
|
||||
height: 72.w,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return SliverPadding(
|
||||
padding: EdgeInsets.fromLTRB(10.w, 34.w, 10.w, 0),
|
||||
sliver: SliverGrid(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) => _buildGiftItem(_gifts[index]),
|
||||
childCount: _gifts.length,
|
||||
),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 3,
|
||||
mainAxisSpacing: 25.w,
|
||||
crossAxisSpacing: 22.w,
|
||||
childAspectRatio: 104 / 130,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildGiftItem(SocialChatGiftRes gift) {
|
||||
final quantity = _giftQuantity(gift);
|
||||
final giftName =
|
||||
(gift.giftName ?? "").trim().isEmpty ? "--" : gift.giftName!.trim();
|
||||
return _buildGradientBorder(
|
||||
radius: 8.w,
|
||||
backgroundColor: _cardBg,
|
||||
endAlpha: 0.62,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.fromLTRB(10.w, 10.w, 10.w, 8.w),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: netImage(
|
||||
url: gift.giftPhoto ?? "",
|
||||
width: 82.w,
|
||||
height: 74.w,
|
||||
fit: BoxFit.contain,
|
||||
noDefaultImg: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 7.w),
|
||||
text(
|
||||
"$giftName*$quantity",
|
||||
fontSize: 13,
|
||||
textColor: Colors.white,
|
||||
maxLines: 1,
|
||||
),
|
||||
SizedBox(height: 5.w),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
_buildCoinIcon(12.w),
|
||||
SizedBox(width: 4.w),
|
||||
Flexible(
|
||||
child: text(
|
||||
_formatNumber(_giftAmount(gift)),
|
||||
fontSize: 11,
|
||||
textColor: Colors.white,
|
||||
maxLines: 1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCoinIcon(double size) {
|
||||
return SizedBox(
|
||||
width: size,
|
||||
height: size,
|
||||
child: ClipRect(
|
||||
child: FittedBox(
|
||||
fit: BoxFit.cover,
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Image.asset("sc_images/room/sc_icon_luckgift_coins_anim.webp"),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bottomPadding = MediaQuery.of(context).padding.bottom + 22.w;
|
||||
return Scaffold(
|
||||
backgroundColor: _profileBg,
|
||||
body: SafeArea(
|
||||
bottom: false,
|
||||
child: CustomScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
slivers: [
|
||||
SliverToBoxAdapter(
|
||||
child: Column(children: [_buildTopBar(), _buildProfileHeader()]),
|
||||
),
|
||||
_buildGiftGrid(),
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: 54.w, bottom: bottomPadding),
|
||||
child: Center(
|
||||
child: text(
|
||||
"No Further Data Available~",
|
||||
fontSize: 13,
|
||||
textColor: const Color(0xffB1B1B1),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,182 +1,250 @@
|
||||
/// id : 0
|
||||
/// giftCode : ""
|
||||
/// giftPhoto : ""
|
||||
/// giftSourceUrl : ""
|
||||
/// giftCandy : 0.0
|
||||
/// giftIntegral : 0.0
|
||||
/// special : ""
|
||||
/// type : ""
|
||||
/// giftTab : ""
|
||||
/// standardId : 0
|
||||
/// explanationGift : false
|
||||
/// account : ""
|
||||
/// userId : 0
|
||||
/// giftName : ""
|
||||
/// expiredTime : 0
|
||||
|
||||
class SocialChatGiftRes {
|
||||
SocialChatGiftRes({
|
||||
String? id,
|
||||
String? giftCode,
|
||||
String? giftPhoto,
|
||||
String? giftSourceUrl,
|
||||
num? giftCandy,
|
||||
num? activityId,
|
||||
num? giftIntegral,
|
||||
String? special,
|
||||
String? type,
|
||||
String? giftTab,
|
||||
String? standardId,
|
||||
String? jumpUrl,
|
||||
String? bannerUrl,
|
||||
bool? explanationGift,
|
||||
String? account,
|
||||
String? userId,
|
||||
String? giftName,
|
||||
String? quantity,
|
||||
num? expiredTime,}){
|
||||
_id = id;
|
||||
_giftCode = giftCode;
|
||||
_giftPhoto = giftPhoto;
|
||||
_giftSourceUrl = giftSourceUrl;
|
||||
_giftCandy = giftCandy;
|
||||
_activityId = activityId;
|
||||
_giftIntegral = giftIntegral;
|
||||
_special = special;
|
||||
_type = type;
|
||||
_giftTab = giftTab;
|
||||
_standardId = standardId;
|
||||
_jumpUrl = jumpUrl;
|
||||
_bannerUrl = bannerUrl;
|
||||
_explanationGift = explanationGift;
|
||||
_account = account;
|
||||
_userId = userId;
|
||||
_giftName = giftName;
|
||||
_quantity = quantity;
|
||||
_expiredTime = expiredTime;
|
||||
}
|
||||
|
||||
SocialChatGiftRes.fromJson(dynamic json) {
|
||||
_id = json['id'];
|
||||
_giftCode = json['giftCode'];
|
||||
_giftPhoto = json['giftPhoto'];
|
||||
_giftSourceUrl = json['giftSourceUrl'];
|
||||
_giftCandy = json['giftCandy'];
|
||||
_activityId = json['activityId'];
|
||||
_giftIntegral = json['giftIntegral'];
|
||||
_special = json['special'];
|
||||
_type = json['type'];
|
||||
_giftTab = json['giftTab'];
|
||||
_standardId = json['standardId'];
|
||||
_jumpUrl = json['jumpUrl'];
|
||||
_bannerUrl = json['bannerUrl'];
|
||||
_explanationGift = json['explanationGift'];
|
||||
_account = json['account'];
|
||||
_userId = json['userId'];
|
||||
_giftName = json['giftName'];
|
||||
_quantity = json['quantity'];
|
||||
_expiredTime = json['expiredTime'];
|
||||
}
|
||||
String? _id;
|
||||
String? _giftCode;
|
||||
String? _giftPhoto;
|
||||
String? _giftSourceUrl;
|
||||
num? _giftCandy;
|
||||
num? _activityId;
|
||||
num? _giftIntegral;
|
||||
String? _special;
|
||||
String? _type;
|
||||
String? _giftTab;
|
||||
String? _standardId;
|
||||
String? _jumpUrl;
|
||||
String? _bannerUrl;
|
||||
bool? _explanationGift;
|
||||
String? _account;
|
||||
String? _userId;
|
||||
String? _giftName;
|
||||
String? _quantity;
|
||||
num? _expiredTime;
|
||||
SocialChatGiftRes copyWith({ String? id,
|
||||
String? giftCode,
|
||||
String? giftPhoto,
|
||||
String? giftSourceUrl,
|
||||
num? giftCandy,
|
||||
num? activityId,
|
||||
num? giftIntegral,
|
||||
String? special,
|
||||
String? type,
|
||||
String? giftTab,
|
||||
String? standardId,
|
||||
String? jumpUrl,
|
||||
String? bannerUrl,
|
||||
bool? explanationGift,
|
||||
String? account,
|
||||
String? userId,
|
||||
String? giftName,
|
||||
String? quantity,
|
||||
num? expiredTime,
|
||||
}) => SocialChatGiftRes( id: id ?? _id,
|
||||
giftCode: giftCode ?? _giftCode,
|
||||
giftPhoto: giftPhoto ?? _giftPhoto,
|
||||
giftSourceUrl: giftSourceUrl ?? _giftSourceUrl,
|
||||
giftCandy: giftCandy ?? _giftCandy,
|
||||
activityId: activityId ?? _activityId,
|
||||
giftIntegral: giftIntegral ?? _giftIntegral,
|
||||
special: special ?? _special,
|
||||
type: type ?? _type,
|
||||
giftTab: giftTab ?? _giftTab,
|
||||
standardId: standardId ?? _standardId,
|
||||
jumpUrl: jumpUrl ?? _jumpUrl,
|
||||
bannerUrl: bannerUrl ?? _bannerUrl,
|
||||
explanationGift: explanationGift ?? _explanationGift,
|
||||
account: account ?? _account,
|
||||
userId: userId ?? _userId,
|
||||
giftName: giftName ?? _giftName,
|
||||
quantity: quantity ?? _quantity,
|
||||
expiredTime: expiredTime ?? _expiredTime,
|
||||
);
|
||||
String? get id => _id;
|
||||
String? get giftCode => _giftCode;
|
||||
String? get giftPhoto => _giftPhoto;
|
||||
String? get giftSourceUrl => _giftSourceUrl;
|
||||
num? get giftCandy => _giftCandy;
|
||||
num? get activityId => _activityId;
|
||||
num? get giftIntegral => _giftIntegral;
|
||||
String? get special => _special;
|
||||
String? get type => _type;
|
||||
String? get giftTab => _giftTab;
|
||||
String? get standardId => _standardId;
|
||||
String? get jumpUrl => _jumpUrl;
|
||||
String? get bannerUrl => _bannerUrl;
|
||||
bool? get explanationGift => _explanationGift;
|
||||
String? get account => _account;
|
||||
String? get userId => _userId;
|
||||
String? get giftName => _giftName;
|
||||
String? get quantity => _quantity;
|
||||
num? get expiredTime => _expiredTime;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['id'] = _id;
|
||||
map['giftCode'] = _giftCode;
|
||||
map['giftPhoto'] = _giftPhoto;
|
||||
map['giftSourceUrl'] = _giftSourceUrl;
|
||||
map['giftCandy'] = _giftCandy;
|
||||
map['activityId'] = _activityId;
|
||||
map['giftIntegral'] = _giftIntegral;
|
||||
map['special'] = _special;
|
||||
map['type'] = _type;
|
||||
map['giftTab'] = _giftTab;
|
||||
map['standardId'] = _standardId;
|
||||
map['jumpUrl'] = _jumpUrl;
|
||||
map['bannerUrl'] = _bannerUrl;
|
||||
map['explanationGift'] = _explanationGift;
|
||||
map['account'] = _account;
|
||||
map['userId'] = _userId;
|
||||
map['giftName'] = _giftName;
|
||||
map['quantity'] = _quantity;
|
||||
map['expiredTime'] = _expiredTime;
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
// id : 0
|
||||
// giftCode : ""
|
||||
// giftPhoto : ""
|
||||
// giftSourceUrl : ""
|
||||
// giftCandy : 0.0
|
||||
// giftIntegral : 0.0
|
||||
// special : ""
|
||||
// type : ""
|
||||
// giftTab : ""
|
||||
// standardId : 0
|
||||
// explanationGift : false
|
||||
// account : ""
|
||||
// userId : 0
|
||||
// giftName : ""
|
||||
// expiredTime : 0
|
||||
|
||||
class SocialChatGiftRes {
|
||||
SocialChatGiftRes({
|
||||
String? id,
|
||||
String? giftCode,
|
||||
String? giftPhoto,
|
||||
String? giftSourceUrl,
|
||||
num? giftCandy,
|
||||
num? activityId,
|
||||
num? giftIntegral,
|
||||
String? special,
|
||||
String? type,
|
||||
String? giftTab,
|
||||
String? standardId,
|
||||
String? jumpUrl,
|
||||
String? bannerUrl,
|
||||
bool? explanationGift,
|
||||
String? account,
|
||||
String? userId,
|
||||
String? giftName,
|
||||
String? quantity,
|
||||
num? expiredTime,
|
||||
}) {
|
||||
_id = id;
|
||||
_giftCode = giftCode;
|
||||
_giftPhoto = giftPhoto;
|
||||
_giftSourceUrl = giftSourceUrl;
|
||||
_giftCandy = giftCandy;
|
||||
_activityId = activityId;
|
||||
_giftIntegral = giftIntegral;
|
||||
_special = special;
|
||||
_type = type;
|
||||
_giftTab = giftTab;
|
||||
_standardId = standardId;
|
||||
_jumpUrl = jumpUrl;
|
||||
_bannerUrl = bannerUrl;
|
||||
_explanationGift = explanationGift;
|
||||
_account = account;
|
||||
_userId = userId;
|
||||
_giftName = giftName;
|
||||
_quantity = quantity;
|
||||
_expiredTime = expiredTime;
|
||||
}
|
||||
|
||||
SocialChatGiftRes.fromJson(dynamic json) {
|
||||
final giftConfig =
|
||||
_giftResMap(json['giftConfig']) ??
|
||||
_giftResMap(json['gift']) ??
|
||||
_giftResMap(json['giftInfo']) ??
|
||||
_giftResMap(json['giftVO']);
|
||||
|
||||
_id = _giftResString(json['id'] ?? giftConfig?['id']);
|
||||
_giftCode = _giftResString(json['giftCode'] ?? giftConfig?['giftCode']);
|
||||
_giftPhoto = _giftResString(
|
||||
json['giftPhoto'] ??
|
||||
json['giftCover'] ??
|
||||
json['cover'] ??
|
||||
giftConfig?['giftPhoto'] ??
|
||||
giftConfig?['giftCover'] ??
|
||||
giftConfig?['cover'],
|
||||
);
|
||||
_giftSourceUrl = _giftResString(
|
||||
json['giftSourceUrl'] ??
|
||||
json['sourceUrl'] ??
|
||||
giftConfig?['giftSourceUrl'] ??
|
||||
giftConfig?['sourceUrl'],
|
||||
);
|
||||
_giftCandy = _giftResNum(
|
||||
json['giftCandy'] ??
|
||||
json['giftAmount'] ??
|
||||
json['giftValue'] ??
|
||||
json['amount'] ??
|
||||
json['price'] ??
|
||||
json['value'] ??
|
||||
json['coins'] ??
|
||||
giftConfig?['giftCandy'] ??
|
||||
giftConfig?['giftAmount'] ??
|
||||
giftConfig?['giftValue'] ??
|
||||
giftConfig?['amount'] ??
|
||||
giftConfig?['price'] ??
|
||||
giftConfig?['value'] ??
|
||||
giftConfig?['coins'],
|
||||
);
|
||||
_activityId = _giftResNum(json['activityId'] ?? giftConfig?['activityId']);
|
||||
_giftIntegral = _giftResNum(
|
||||
json['giftIntegral'] ??
|
||||
json['integral'] ??
|
||||
giftConfig?['giftIntegral'] ??
|
||||
giftConfig?['integral'],
|
||||
);
|
||||
_special = _giftResString(json['special'] ?? giftConfig?['special']);
|
||||
_type = _giftResString(json['type'] ?? giftConfig?['type']);
|
||||
_giftTab = _giftResString(json['giftTab'] ?? giftConfig?['giftTab']);
|
||||
_standardId = _giftResString(
|
||||
json['standardId'] ?? giftConfig?['standardId'],
|
||||
);
|
||||
_jumpUrl = _giftResString(json['jumpUrl'] ?? giftConfig?['jumpUrl']);
|
||||
_bannerUrl = _giftResString(json['bannerUrl'] ?? giftConfig?['bannerUrl']);
|
||||
_explanationGift = json['explanationGift'];
|
||||
_account = _giftResString(json['account'] ?? giftConfig?['account']);
|
||||
_userId = _giftResString(json['userId'] ?? giftConfig?['userId']);
|
||||
_giftName = _giftResString(
|
||||
json['giftName'] ??
|
||||
json['name'] ??
|
||||
giftConfig?['giftName'] ??
|
||||
giftConfig?['name'],
|
||||
);
|
||||
_quantity = _giftResString(
|
||||
json['quantity'] ?? json['giftQuantity'] ?? json['count'] ?? json['num'],
|
||||
);
|
||||
_expiredTime = _giftResNum(
|
||||
json['expiredTime'] ?? giftConfig?['expiredTime'],
|
||||
);
|
||||
}
|
||||
String? _id;
|
||||
String? _giftCode;
|
||||
String? _giftPhoto;
|
||||
String? _giftSourceUrl;
|
||||
num? _giftCandy;
|
||||
num? _activityId;
|
||||
num? _giftIntegral;
|
||||
String? _special;
|
||||
String? _type;
|
||||
String? _giftTab;
|
||||
String? _standardId;
|
||||
String? _jumpUrl;
|
||||
String? _bannerUrl;
|
||||
bool? _explanationGift;
|
||||
String? _account;
|
||||
String? _userId;
|
||||
String? _giftName;
|
||||
String? _quantity;
|
||||
num? _expiredTime;
|
||||
SocialChatGiftRes copyWith({
|
||||
String? id,
|
||||
String? giftCode,
|
||||
String? giftPhoto,
|
||||
String? giftSourceUrl,
|
||||
num? giftCandy,
|
||||
num? activityId,
|
||||
num? giftIntegral,
|
||||
String? special,
|
||||
String? type,
|
||||
String? giftTab,
|
||||
String? standardId,
|
||||
String? jumpUrl,
|
||||
String? bannerUrl,
|
||||
bool? explanationGift,
|
||||
String? account,
|
||||
String? userId,
|
||||
String? giftName,
|
||||
String? quantity,
|
||||
num? expiredTime,
|
||||
}) => SocialChatGiftRes(
|
||||
id: id ?? _id,
|
||||
giftCode: giftCode ?? _giftCode,
|
||||
giftPhoto: giftPhoto ?? _giftPhoto,
|
||||
giftSourceUrl: giftSourceUrl ?? _giftSourceUrl,
|
||||
giftCandy: giftCandy ?? _giftCandy,
|
||||
activityId: activityId ?? _activityId,
|
||||
giftIntegral: giftIntegral ?? _giftIntegral,
|
||||
special: special ?? _special,
|
||||
type: type ?? _type,
|
||||
giftTab: giftTab ?? _giftTab,
|
||||
standardId: standardId ?? _standardId,
|
||||
jumpUrl: jumpUrl ?? _jumpUrl,
|
||||
bannerUrl: bannerUrl ?? _bannerUrl,
|
||||
explanationGift: explanationGift ?? _explanationGift,
|
||||
account: account ?? _account,
|
||||
userId: userId ?? _userId,
|
||||
giftName: giftName ?? _giftName,
|
||||
quantity: quantity ?? _quantity,
|
||||
expiredTime: expiredTime ?? _expiredTime,
|
||||
);
|
||||
String? get id => _id;
|
||||
String? get giftCode => _giftCode;
|
||||
String? get giftPhoto => _giftPhoto;
|
||||
String? get giftSourceUrl => _giftSourceUrl;
|
||||
num? get giftCandy => _giftCandy;
|
||||
num? get activityId => _activityId;
|
||||
num? get giftIntegral => _giftIntegral;
|
||||
String? get special => _special;
|
||||
String? get type => _type;
|
||||
String? get giftTab => _giftTab;
|
||||
String? get standardId => _standardId;
|
||||
String? get jumpUrl => _jumpUrl;
|
||||
String? get bannerUrl => _bannerUrl;
|
||||
bool? get explanationGift => _explanationGift;
|
||||
String? get account => _account;
|
||||
String? get userId => _userId;
|
||||
String? get giftName => _giftName;
|
||||
String? get quantity => _quantity;
|
||||
num? get expiredTime => _expiredTime;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['id'] = _id;
|
||||
map['giftCode'] = _giftCode;
|
||||
map['giftPhoto'] = _giftPhoto;
|
||||
map['giftSourceUrl'] = _giftSourceUrl;
|
||||
map['giftCandy'] = _giftCandy;
|
||||
map['activityId'] = _activityId;
|
||||
map['giftIntegral'] = _giftIntegral;
|
||||
map['special'] = _special;
|
||||
map['type'] = _type;
|
||||
map['giftTab'] = _giftTab;
|
||||
map['standardId'] = _standardId;
|
||||
map['jumpUrl'] = _jumpUrl;
|
||||
map['bannerUrl'] = _bannerUrl;
|
||||
map['explanationGift'] = _explanationGift;
|
||||
map['account'] = _account;
|
||||
map['userId'] = _userId;
|
||||
map['giftName'] = _giftName;
|
||||
map['quantity'] = _quantity;
|
||||
map['expiredTime'] = _expiredTime;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic>? _giftResMap(dynamic value) {
|
||||
if (value is Map<String, dynamic>) return value;
|
||||
if (value is Map) return Map<String, dynamic>.from(value);
|
||||
return null;
|
||||
}
|
||||
|
||||
String? _giftResString(dynamic value) {
|
||||
if (value == null) return null;
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
num? _giftResNum(dynamic value) {
|
||||
if (value == null) return null;
|
||||
if (value is num) return value;
|
||||
return num.tryParse(value.toString());
|
||||
}
|
||||
|
||||
BIN
sc_images/person/sc_icon_profile_following.png
Normal file
BIN
sc_images/person/sc_icon_profile_following.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
BIN
sc_images/person/sc_icon_profile_gift_wall_title.png
Normal file
BIN
sc_images/person/sc_icon_profile_gift_wall_title.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.0 KiB |
BIN
sc_images/person/sc_icon_profile_wall_of_honors_title.png
Normal file
BIN
sc_images/person/sc_icon_profile_wall_of_honors_title.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
9
需求进度.md
9
需求进度.md
@ -9,7 +9,14 @@
|
||||
- 第一步已去掉个人资料页加载阶段的骨架屏:`ref.userProfile` 或拉黑状态还没返回时,正文区域改为空占位,不再展示灰色骨架块;资料返回后仍按原来的 `Header + About/Giftwall Tab` 结构渲染。
|
||||
- 第二步已将个人资料页上方默认背景图从旧的 `sc_images/person/sc_icon_my_head_bg_defalt.png` 切换为新素材 `sc_images/person/sc_icon_profile_card_default_bg.png`;用户已有可用 `backgroundPhotos` 时仍优先展示用户自定义背景。
|
||||
- 第三步已调整头部资料区对齐:深绿资料区去掉顶部圆角改为矩形;头像/头像框移动到中间;用户名居中单独一行;ID、复制按钮、性别年龄和国旗收成同一行居中展示,复制按钮点击后仍写入剪贴板并提示 copied。
|
||||
- 验证进度:`dart format lib/modules/user/profile/person_detail_page.dart` 已执行;`flutter analyze --no-fatal-infos lib/modules/user/profile/person_detail_page.dart` 无 error/warning,仅保留该旧文件原有的 info 级代码风格提示。下一步再继续逐项对齐资料页 UI。
|
||||
- 第四步已继续对齐设计稿:ID 行下方新增一行徽章预留占位;用户 `autograph/bio` 已前移到徽章占位下方并按 `Introduction:` 展示;Visitor/Follow/Fans 统计块改为 `355*51`、圆角 `8` 的纵向渐隐绿色描边,中间分割线改为高度 `28` 的白色纵向渐隐线。
|
||||
- 第五步已把个人资料页大背景切到 `#072121`,Visitor/Follow/Fans 渐变卡内部保持 `#08251E`;下方 `About me / Gift wall` tab 结构已移除,改为设计图同款纵向 `Wall Of Honors` + `Gift Wall` section。`Wall Of Honors` 现用语音房 music 空态图做占位;两个 section 的 item 统一为 `76*76`、圆角 `8`、纵向渐隐绿色描边,`Gift Wall` 继续复用原礼物墙接口数据。
|
||||
- 第六步已继续对稿:`Wall Of Honors` 与 `Gift Wall` 标题改为图片资源渲染,新增 `sc_icon_profile_wall_of_honors_title.png` 与 `sc_icon_profile_gift_wall_title.png`;荣誉/礼物 item 的描边底部透明度提高,解决下边线不明显的问题;非本人资料页底部操作区改为设计稿样式,`Follow` 为 `104*36`、`Chat` 为 `215*36`,同为 `#0A342D` 背景、`#18F2B1` 1px 边框、22 圆角和 28 图标;在房间用户会额外显示悬浮 `343*38` 房间入口,左侧头像昵称,右侧 `69*27` Enter 按钮,和底部按钮行保持 43 间距。
|
||||
- 第七步已按 2026-05-07 最新反馈微调:重新覆盖 `Wall Of Honors / Gift Wall` 标题 PNG 风格;Visitor/Follow/Fans 统计卡底部描边透明度提高;荣誉/礼物 item 底部描边透明度继续提高;底部按钮区增加 `#072121`、高度 `93` 的背景;`Follow` 宽度增至 `154`,`Chat` 宽度缩至 `165`;Chat 图标改回旧图标 `sc_icon_person_tochat.png`;Following 状态新增并使用 `sc_icon_profile_following.png`。
|
||||
- 第八步已继续按 2026-05-07 最新反馈收口:确认 `Wall Of Honors / Gift Wall` 标题直接使用用户替换后的 PNG 素材,不再自绘标题;非本人资料页底部按钮整体上移并扩大底部安全留白,减少被系统 Home Indicator 遮挡;`Gift Wall` 标题点击已进入新的独立礼物墙页面,页面包含返回栏、头像头像框、昵称、Received/Total Value 统计卡、三列礼物列表和底部 `No Further Data Available~` 文案。
|
||||
- 第九步已修正 Gift Wall 详情页初版问题:顶部导航栏补齐满屏宽度,避免返回箭头挤进标题;头像缩小到 `104`,昵称字号改为正常单次缩放;金币展示改用现有单枚金币动效素材;礼物数据模型兼容 `giftConfig/name/amount/price/value/coins/giftQuantity` 等接口字段,避免礼物名和金额接口有值但页面显示 `-- / 0`。
|
||||
- 第十步已为 Gift Wall 详情页补齐礼物名称/金额解析链路:礼物墙接口返回后会再用全局 `/gift/list` 配置表按 `standardId / id / giftCode / giftPhoto` 做二次匹配,补齐缺失的 `giftName / giftCandy / giftIntegral / giftPhoto`,用于单个礼物卡片和 `Total Value` 统计。
|
||||
- 验证进度:`dart format lib/modules/user/profile/profile_gift_wall_detail_page.dart lib/shared/business_logic/models/res/gift_res.dart` 已执行;`flutter analyze --no-fatal-infos lib/modules/user/profile/profile_gift_wall_detail_page.dart lib/shared/business_logic/models/res/gift_res.dart lib/modules/user/profile/person_detail_page.dart lib/modules/index/main_route.dart` 无 error/warning,仅保留旧文件原有的 info 级代码风格提示。下一步再继续逐项对齐资料页 UI。
|
||||
|
||||
## 本轮 Android 语言房音乐功能(进行中)
|
||||
- 已查阅桌面《Android音乐添加流程.md》,并产出可执行方案文档 `docs/android-room-music-execution-plan.md`,覆盖功能范围、模块拆分、Android 权限、声网混音链路、UI 流程、风险点和 GPT 5.5 预估开发工时。
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user