个人资料卡

This commit is contained in:
roxy 2026-05-07 14:25:07 +08:00
parent 23748737a2
commit e41cb3fb24
8 changed files with 1296 additions and 645 deletions

View File

@ -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

View 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),
),
),
),
),
],
),
),
);
}
}

View File

@ -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());
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -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 预估开发工时。